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;
195 int buttons, icon, retval;
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 retval = MyAppCallback_messageBox(str, title, buttons, icon);
223 return (retval ? Qtrue : Qfalse);
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 * cmd is either a single string of an array of string. If it is a single string, then
1002 * it will be given to wxExecute as a single argument. In this case, the string can be
1003 * split into arguments by whitespace. If this behavior is not intended, then use an array
1004 * containing a single string.
1005 * A callback proc can be given, which is called periodically during execution. If the proc returns
1006 * nil or false, then the execution will be interrupted.
1007 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
1008 * filename begins with ">>", then the message will be appended to the file.
1009 * If the filename is "/dev/null" or "NUL", then the message will be lost.
1010 * If the argument is nil, then the message will be sent to the Ruby console.
1013 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
1015 VALUE cmd, procname, cproc, stdout_val, stderr_val;
1016 VALUE save_interruptFlag;
1017 int n, exitstatus, pid;
1019 const char *pnamestr, **cmdargv;
1020 FILE *fpout, *fperr;
1022 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
1024 if (stdout_val == Qnil) {
1027 sout = StringValuePtr(stdout_val);
1028 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
1031 if (strncmp(sout, ">>", 2) == 0) {
1033 fpout = fopen(sout, "a");
1037 fpout = fopen(sout, "w");
1040 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
1043 if (stderr_val == Qnil) {
1046 serr = StringValuePtr(stderr_val);
1047 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
1050 if (strncmp(serr, ">>", 2) == 0) {
1052 fpout = fopen(serr, "a");
1056 fperr = fopen(serr, "w");
1059 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
1063 save_interruptFlag = s_SetInterruptFlag(self, Qnil);
1064 if (procname != Qnil)
1065 pnamestr = StringValuePtr(procname);
1066 else pnamestr = NULL;
1067 if (rb_obj_is_kind_of(cmd, rb_cString)) {
1068 cmdargv = calloc(sizeof(cmdargv[0]), 3);
1069 cmdargv[0] = StringValuePtr(cmd);
1073 cmd = rb_ary_to_ary(cmd);
1074 cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
1075 for (n = 0; n < RARRAY_LEN(cmd); n++) {
1076 cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
1080 n = MyAppCallback_callSubProcess(cmdargv, pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
1081 s_SetInterruptFlag(self, save_interruptFlag);
1084 if (fpout != NULL && fpout != (FILE *)1)
1086 if (fperr != NULL && fperr != (FILE *)1)
1098 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
1101 s_Kernel_Backquote(VALUE self, VALUE cmd)
1104 int n, exitstatus, pid;
1106 const char *cmdargv[3];
1107 cmdargv[0] = StringValuePtr(cmd);
1110 n = MyAppCallback_callSubProcess(cmdargv, NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
1111 /* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
1112 if (n >= 0 && buf != NULL) {
1113 val = Ruby_NewEncodedStringValue(buf, 0);
1116 val = Ruby_NewEncodedStringValue("", 0);
1118 rb_last_status_set(exitstatus, pid);
1122 #pragma mark ====== User defaults ======
1126 * get_global_settings(key)
1128 * Get a setting data for key from the application preferences.
1131 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
1133 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
1135 VALUE retval = rb_eval_string(p);
1143 * set_global_settings(key, value)
1145 * Set a setting data for key to the application preferences.
1148 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
1150 VALUE sval = rb_inspect(value);
1151 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
1155 #pragma mark ====== IO extension ======
1158 s_Ruby_str_encode_protected(VALUE val)
1160 return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
1161 ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
1168 * A gets variant that works for CR, LF, and CRLF end-of-line characters.
1171 s_IO_gets_any_eol(VALUE self)
1173 VALUE val, val2, cval;
1176 static ID id_getbyte = 0, id_ungetbyte;
1177 if (id_getbyte == 0) {
1178 id_getbyte = rb_intern("getbyte");
1179 id_ungetbyte = rb_intern("ungetbyte");
1183 while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
1184 c = NUM2INT(rb_Integer(cval));
1186 cval = rb_funcall(self, id_getbyte, 0);
1188 c = NUM2INT(rb_Integer(cval));
1190 rb_funcall(self, id_ungetbyte, 1, cval);
1193 } else if (c != 0x0a) {
1198 val = rb_str_new(buf, i);
1200 rb_str_append(val, rb_str_new(buf, i));
1205 if (cval == Qnil && i == 0 && val == Qnil)
1206 return Qnil; /* End of file */
1209 val = rb_str_new(buf, i);
1211 rb_str_append(val, rb_str_new(buf, i));
1212 val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
1216 /* Needs a end-of-line mark */
1217 cval = rb_gv_get("$/");
1218 rb_str_append(val, cval);
1220 rb_gv_set("$_", val);
1224 #pragma mark ====== Utility functions (protected funcall) ======
1226 struct Ruby_funcall2_record {
1234 s_Ruby_funcall2_sub(VALUE data)
1236 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
1237 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
1241 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
1243 struct Ruby_funcall2_record rec;
1248 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
1252 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
1254 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
1257 #pragma mark ====== ParameterRef Class ======
1260 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
1264 Data_Get_Struct(self, ParameterRef, pref);
1266 *typep = pref->parType;
1267 if (pref->parType == kElementParType) {
1268 up = (UnionPar *)&gElementParameters[pref->idx];
1270 up = ParameterRefGetPar(pref);
1271 if (checkEditable) {
1273 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
1274 if (up->bond.src != 0 && up->bond.src != -1)
1275 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1282 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1286 Data_Get_Struct(self, ParameterRef, pref);
1287 if (pref->mol == NULL)
1289 up = ParameterRefGetPar(pref);
1290 if (key != s_SourceSym)
1291 up->bond.src = 0; /* Becomes automatically molecule-local */
1292 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1295 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1296 MolActionCallback_registerUndo(pref->mol, act);
1297 MoleculeCallback_notifyModification(pref->mol, 0);
1298 pref->mol->needsMDRebuild = 1;
1303 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1305 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1307 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1309 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1313 s_AtomTypeIndexFromValue(VALUE val)
1315 if (rb_obj_is_kind_of(val, rb_cNumeric))
1316 return NUM2INT(val);
1318 return AtomTypeEncodeToUInt(StringValuePtr(val));
1321 static const char *s_ParameterTypeNames[] = {
1322 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1324 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1327 s_ParTypeFromValue(VALUE val)
1331 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1332 if (s_ParameterTypeIDs[0] == 0) {
1333 for (i = 0; i < n; i++)
1334 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1336 valid = rb_to_id(val);
1337 for (i = 0; i < n; i++) {
1338 if (valid == s_ParameterTypeIDs[i]) {
1340 return kElementParType;
1341 else return kFirstParType + i;
1344 return kInvalidParType;
1351 * Get the index in the parameter list.
1353 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1355 Data_Get_Struct(self, ParameterRef, pref);
1356 return INT2NUM(pref->idx);
1361 * par_type -> String
1363 * Get the parameter type, like "bond", "angle", etc.
1365 static VALUE s_ParameterRef_GetParType(VALUE self) {
1367 s_UnionParFromValue(self, &tp, 0);
1368 if (tp == kElementParType)
1369 return Ruby_NewEncodedStringValue2("element");
1370 tp -= kFirstParType;
1371 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1372 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
1373 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1378 * atom_type -> String or Array of String
1379 * atom_types -> String or Array of String
1381 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1382 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1383 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1384 * The atom type may be "X", which is a wildcard that matches any atom type.
1386 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1391 up = s_UnionParFromValue(self, &tp, 0);
1392 n = ParameterGetAtomTypes(tp, up, types);
1394 rb_raise(rb_eMolbyError, "invalid member atom_types");
1395 for (i = 0; i < n; i++) {
1396 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1397 vals[i] = INT2NUM(types[i]);
1399 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1404 return rb_ary_new4(n, vals);
1411 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1413 static VALUE s_ParameterRef_GetK(VALUE self) {
1417 up = s_UnionParFromValue(self, &tp, 0);
1420 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1422 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1423 case kDihedralParType:
1424 case kImproperParType:
1425 if (up->torsion.mult == 1)
1426 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1427 n = up->torsion.mult;
1430 for (i = 0; i < n; i++)
1431 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1432 return rb_ary_new4(n, vals);
1434 rb_raise(rb_eMolbyError, "invalid member k");
1442 * Get the equilibrium bond length. Only available for bond parameters.
1444 static VALUE s_ParameterRef_GetR0(VALUE self) {
1447 up = s_UnionParFromValue(self, &tp, 0);
1448 if (tp == kBondParType)
1449 return rb_float_new(up->bond.r0);
1450 else rb_raise(rb_eMolbyError, "invalid member r0");
1457 * Get the equilibrium angle (in degree). Only available for angle parameters.
1459 static VALUE s_ParameterRef_GetA0(VALUE self) {
1462 up = s_UnionParFromValue(self, &tp, 0);
1463 if (tp == kAngleParType)
1464 return rb_float_new(up->angle.a0 * kRad2Deg);
1465 else rb_raise(rb_eMolbyError, "invalid member a0");
1472 * Get the multiplicity. Only available for dihedral and improper parameters.
1473 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1475 static VALUE s_ParameterRef_GetMult(VALUE self) {
1478 up = s_UnionParFromValue(self, &tp, 0);
1479 if (tp == kDihedralParType || tp == kImproperParType)
1480 return rb_float_new(up->torsion.mult);
1481 else rb_raise(rb_eMolbyError, "invalid member mult");
1486 * period -> Integer or Array of Integers
1488 * Get the periodicity. Only available for dihedral and improper parameters.
1489 * If the multiplicity is larger than 1, then an array of integers is returned.
1490 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1492 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1496 up = s_UnionParFromValue(self, &tp, 0);
1497 if (tp == kDihedralParType || tp == kImproperParType) {
1498 if (up->torsion.mult == 1)
1499 return INT2NUM(up->torsion.period[0]);
1500 n = up->torsion.mult;
1503 for (i = 0; i < n; i++)
1504 vals[i] = INT2NUM(up->torsion.period[i]);
1505 return rb_ary_new4(n, vals);
1506 } else rb_raise(rb_eMolbyError, "invalid member period");
1511 * phi0 -> Float or Array of Floats
1513 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1514 * If the multiplicity is larger than 1, then an array of floats is returned.
1515 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1517 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1521 up = s_UnionParFromValue(self, &tp, 0);
1522 if (tp == kDihedralParType || tp == kImproperParType) {
1523 if (up->torsion.mult == 1)
1524 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1525 n = up->torsion.mult;
1528 for (i = 0; i < n; i++)
1529 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1530 return rb_ary_new4(n, vals);
1531 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1538 * Get the "A" value for the van der Waals parameter.
1541 static VALUE s_ParameterRef_GetA(VALUE self) {
1544 up = s_UnionParFromValue(self, &tp, 0);
1545 if (tp == kVdwParType)
1546 return rb_float_new(up->vdw.A);
1547 else if (tp == kVdwPairParType)
1548 return rb_float_new(up->vdwp.A);
1549 else rb_raise(rb_eMolbyError, "invalid member A");
1557 * Get the "B" value for the van der Waals parameter.
1560 static VALUE s_ParameterRef_GetB(VALUE self) {
1563 up = s_UnionParFromValue(self, &tp, 0);
1564 if (tp == kVdwParType)
1565 return rb_float_new(up->vdw.B);
1566 else if (tp == kVdwPairParType)
1567 return rb_float_new(up->vdwp.B);
1568 else rb_raise(rb_eMolbyError, "invalid member B");
1576 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1578 static VALUE s_ParameterRef_GetReq(VALUE self) {
1581 /* Double a, b, r; */
1583 up = s_UnionParFromValue(self, &tp, 0);
1584 if (tp == kVdwParType) {
1588 } else if (tp == kVdwPairParType) {
1592 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1593 /* if (a == 0.0 || b == 0.0) */
1594 return rb_float_new(r);
1595 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1602 * Get the minimum energy for the van der Waals parameter.
1604 static VALUE s_ParameterRef_GetEps(VALUE self) {
1609 up = s_UnionParFromValue(self, &tp, 0);
1610 if (tp == kVdwParType) {
1614 } else if (tp == kVdwPairParType) {
1618 } else rb_raise(rb_eMolbyError, "invalid member eps");
1619 /* if (a == 0.0 || b == 0.0) */
1620 return rb_float_new(eps * INTERNAL2KCAL);
1621 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1628 * Get the "A" value for the 1-4 van der Waals parameter.
1631 static VALUE s_ParameterRef_GetA14(VALUE self) {
1634 up = s_UnionParFromValue(self, &tp, 0);
1635 if (tp == kVdwParType)
1636 return rb_float_new(up->vdw.A14);
1637 else if (tp == kVdwPairParType)
1638 return rb_float_new(up->vdwp.A14);
1639 else rb_raise(rb_eMolbyError, "invalid member A14");
1647 * Get the "B" value for the 1-4 van der Waals parameter.
1650 static VALUE s_ParameterRef_GetB14(VALUE self) {
1653 up = s_UnionParFromValue(self, &tp, 0);
1654 if (tp == kVdwParType)
1655 return rb_float_new(up->vdw.B14);
1656 else if (tp == kVdwPairParType)
1657 return rb_float_new(up->vdwp.B14);
1658 else rb_raise(rb_eMolbyError, "invalid member B14");
1666 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1668 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1671 /* Double a, b, r; */
1673 up = s_UnionParFromValue(self, &tp, 0);
1674 if (tp == kVdwParType) {
1678 } else if (tp == kVdwPairParType) {
1679 /* a = up->vdwp.A14;
1680 b = up->vdwp.B14; */
1681 r = up->vdwp.r_eq14;
1682 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1683 /* if (a == 0.0 || b == 0.0) */
1684 return rb_float_new(r);
1685 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1692 * Get the minimum energy for the 1-4 van der Waals parameter.
1694 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1699 up = s_UnionParFromValue(self, &tp, 0);
1700 if (tp == kVdwParType) {
1703 eps = up->vdw.eps14;
1704 } else if (tp == kVdwPairParType) {
1705 /* a = up->vdwp.A14;
1706 b = up->vdwp.B14; */
1707 eps = up->vdwp.eps14;
1708 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1709 /* if (a == 0.0 || b == 0.0) */
1710 return rb_float_new(eps * INTERNAL2KCAL);
1711 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1718 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1720 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1723 up = s_UnionParFromValue(self, &tp, 0);
1724 if (tp == kVdwCutoffParType)
1725 return rb_float_new(up->vdwcutoff.cutoff);
1726 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1733 * Get the atomic (covalent) radius for the element parameter.
1735 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1738 up = s_UnionParFromValue(self, &tp, 0);
1739 if (tp == kElementParType)
1740 return rb_float_new(up->atom.radius);
1741 else rb_raise(rb_eMolbyError, "invalid member radius");
1746 * vdw_radius -> Float
1748 * Get the van der Waals radius for the element parameter. (0 if not given)
1750 static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
1753 up = s_UnionParFromValue(self, &tp, 0);
1754 if (tp == kElementParType)
1755 return rb_float_new(up->atom.vdw_radius);
1756 else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
1761 * color -> [Float, Float, Float]
1763 * Get the rgb color for the element parameter.
1765 static VALUE s_ParameterRef_GetColor(VALUE self) {
1768 up = s_UnionParFromValue(self, &tp, 0);
1769 if (tp == kElementParType)
1770 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));
1771 else rb_raise(rb_eMolbyError, "invalid member color");
1776 * atomic_number -> Integer
1778 * Get the atomic number for the vdw or element parameter.
1780 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1783 up = s_UnionParFromValue(self, &tp, 0);
1784 if (tp == kElementParType)
1785 return INT2NUM(up->atom.number);
1786 else if (tp == kVdwParType)
1787 return INT2NUM(up->vdw.atomicNumber);
1788 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1795 * Get the name for the element parameter.
1797 static VALUE s_ParameterRef_GetName(VALUE self) {
1800 up = s_UnionParFromValue(self, &tp, 0);
1801 if (tp == kElementParType) {
1803 strncpy(name, up->atom.name, 4);
1805 return Ruby_NewEncodedStringValue2(name);
1806 } else rb_raise(rb_eMolbyError, "invalid member name");
1813 * Get the atomic weight for the element parameter.
1815 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1818 up = s_UnionParFromValue(self, &tp, 0);
1819 if (tp == kElementParType)
1820 return rb_float_new(up->atom.weight);
1821 else if (tp == kVdwParType)
1822 return rb_float_new(up->vdw.weight);
1823 else rb_raise(rb_eMolbyError, "invalid member weight");
1828 * fullname -> String
1830 * Get the full name for the element parameter.
1832 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1835 up = s_UnionParFromValue(self, &tp, 0);
1836 if (tp == kElementParType) {
1838 strncpy(fullname, up->atom.fullname, 15);
1840 return Ruby_NewEncodedStringValue2(fullname);
1841 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1848 * Get the comment for the parameter.
1850 static VALUE s_ParameterRef_GetComment(VALUE self) {
1853 up = s_UnionParFromValue(self, &tp, 0);
1857 else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
1864 * Get the source string for the parameter. Returns false for undefined parameter,
1865 * and nil for "local" parameter that is specific for the molecule.
1867 static VALUE s_ParameterRef_GetSource(VALUE self) {
1870 up = s_UnionParFromValue(self, &tp, 0);
1873 return Qfalse; /* undefined */
1875 return Qnil; /* local */
1876 else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
1880 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1887 if (rb_obj_is_kind_of(val, rb_cString)) {
1888 char *s = StringValuePtr(val);
1890 for (i = 0; i < n; i++) {
1893 /* Skip leading separaters */
1894 while (*s == '-' || *s == ' ' || *s == '\t')
1896 for (p = s; *p != 0; p++) {
1897 if (*p == '-' || *p == ' ' || *p == '\t')
1901 if (len >= sizeof(buf))
1902 len = sizeof(buf) - 1;
1903 strncpy(buf, s, len);
1905 /* Skip trailing blanks */
1906 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1909 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1910 if (buf[0] >= '0' && buf[0] <= '9')
1911 types[i] = atoi(buf);
1913 types[i] = AtomTypeEncodeToUInt(buf);
1914 if (p == NULL || *p == 0) {
1920 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1923 val = rb_ary_to_ary(val);
1924 if (RARRAY_LEN(val) != n)
1925 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1926 valp = RARRAY_PTR(val);
1928 for (i = 0; i < n; i++) {
1929 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1930 types[i] = NUM2INT(rb_Integer(valp[i]));
1932 VALUE sval = valp[i];
1933 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1938 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1943 up = s_UnionParFromValue(self, &tp, 1);
1944 oldval = s_ParameterRef_GetAtomTypes(self);
1945 oldsrc = up->bond.src;
1948 s_ScanAtomTypes(val, 2, types);
1949 up->bond.type1 = types[0];
1950 up->bond.type2 = types[1];
1953 s_ScanAtomTypes(val, 3, types);
1954 up->angle.type1 = types[0];
1955 up->angle.type2 = types[1];
1956 up->angle.type3 = types[2];
1958 case kDihedralParType:
1959 case kImproperParType:
1960 s_ScanAtomTypes(val, 4, types);
1961 up->torsion.type1 = types[0];
1962 up->torsion.type2 = types[1];
1963 up->torsion.type3 = types[2];
1964 up->torsion.type4 = types[3];
1967 s_ScanAtomTypes(val, 1, types);
1968 up->vdw.type1 = types[0];
1970 case kVdwPairParType:
1971 s_ScanAtomTypes(val, 2, types);
1972 up->vdwp.type1 = types[0];
1973 up->vdwp.type2 = types[1];
1975 case kVdwCutoffParType:
1976 s_ScanAtomTypes(val, 2, types);
1977 up->vdwcutoff.type1 = types[0];
1978 up->vdwcutoff.type2 = types[1];
1983 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1987 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1989 Int tp, i, n, oldsrc;
1990 VALUE *valp, oldval;
1991 up = s_UnionParFromValue(self, &tp, 1);
1992 oldval = s_ParameterRef_GetK(self);
1993 oldsrc = up->bond.src;
1996 val = rb_Float(val);
1997 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
2000 val = rb_Float(val);
2001 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
2003 case kDihedralParType:
2004 case kImproperParType:
2005 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2006 up->torsion.mult = 1;
2007 val = rb_Float(val);
2008 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
2011 n = up->torsion.mult;
2014 val = rb_ary_to_ary(val);
2015 if (RARRAY_LEN(val) != n)
2016 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2017 valp = RARRAY_PTR(val);
2018 for (i = 0; i < n; i++) {
2019 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
2023 rb_raise(rb_eMolbyError, "invalid member k");
2025 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
2029 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
2033 up = s_UnionParFromValue(self, &tp, 1);
2034 oldval = s_ParameterRef_GetR0(self);
2035 oldsrc = up->bond.src;
2036 if (tp == kBondParType) {
2037 val = rb_Float(val);
2038 up->bond.r0 = NUM2DBL(val);
2039 } else rb_raise(rb_eMolbyError, "invalid member r0");
2040 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
2044 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
2048 up = s_UnionParFromValue(self, &tp, 1);
2049 oldval = s_ParameterRef_GetA0(self);
2050 oldsrc = up->bond.src;
2051 if (tp == kAngleParType) {
2052 val = rb_Float(val);
2053 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
2054 } else rb_raise(rb_eMolbyError, "invalid member a0");
2055 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
2059 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
2063 up = s_UnionParFromValue(self, &tp, 1);
2064 oldval = s_ParameterRef_GetMult(self);
2065 oldsrc = up->bond.src;
2066 if (tp == kDihedralParType || tp == kImproperParType) {
2068 val = rb_Integer(val);
2071 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
2072 up->torsion.mult = i;
2073 } else rb_raise(rb_eMolbyError, "invalid member mult");
2074 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
2078 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
2080 Int tp, i, n, oldsrc;
2081 VALUE *valp, oldval;
2082 up = s_UnionParFromValue(self, &tp, 1);
2083 oldval = s_ParameterRef_GetPeriod(self);
2084 oldsrc = up->bond.src;
2085 if (tp == kDihedralParType || tp == kImproperParType) {
2086 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2087 up->torsion.mult = 1;
2088 val = rb_Integer(val);
2089 up->torsion.period[0] = NUM2INT(val);
2091 n = up->torsion.mult;
2094 val = rb_ary_to_ary(val);
2095 if (RARRAY_LEN(val) != n)
2096 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
2097 valp = RARRAY_PTR(val);
2098 for (i = 0; i < n; i++) {
2099 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
2102 } else rb_raise(rb_eMolbyError, "invalid member period");
2103 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
2107 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
2109 Int tp, i, n, oldsrc;
2110 VALUE *valp, oldval;
2111 up = s_UnionParFromValue(self, &tp, 1);
2112 oldval = s_ParameterRef_GetPhi0(self);
2113 oldsrc = up->bond.src;
2114 if (tp == kDihedralParType || tp == kImproperParType) {
2115 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
2116 up->torsion.mult = 1;
2117 val = rb_Float(val);
2118 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
2120 n = up->torsion.mult;
2123 val = rb_ary_to_ary(val);
2124 if (RARRAY_LEN(val) != n)
2125 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
2126 valp = RARRAY_PTR(val);
2127 for (i = 0; i < n; i++)
2128 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
2130 } else rb_raise(rb_eMolbyError, "invalid member phi0");
2131 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
2136 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
2141 up = s_UnionParFromValue(self, &tp, 1);
2142 oldval = s_ParameterRef_GetA(self);
2143 oldsrc = up->bond.src;
2144 val = rb_Float(val);
2146 if (tp == kVdwParType)
2148 else if (tp == kVdwPairParType)
2150 else rb_raise(rb_eMolbyError, "invalid member A");
2151 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
2155 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
2160 up = s_UnionParFromValue(self, &tp, 1);
2161 oldval = s_ParameterRef_GetB(self);
2162 oldsrc = up->bond.src;
2163 val = rb_Float(val);
2165 if (tp == kVdwParType)
2167 else if (tp == kVdwPairParType)
2169 else rb_raise(rb_eMolbyError, "invalid member B");
2170 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
2175 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
2180 up = s_UnionParFromValue(self, &tp, 1);
2181 oldval = s_ParameterRef_GetReq(self);
2182 oldsrc = up->bond.src;
2183 val = rb_Float(val);
2185 if (tp == kVdwParType) {
2187 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
2188 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
2189 } else if (tp == kVdwPairParType) {
2191 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
2192 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
2193 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
2194 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
2198 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
2203 up = s_UnionParFromValue(self, &tp, 1);
2204 oldval = s_ParameterRef_GetEps(self);
2205 oldsrc = up->bond.src;
2206 val = rb_Float(val);
2207 e = NUM2DBL(val) * KCAL2INTERNAL;
2208 if (tp == kVdwParType) {
2210 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
2211 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
2212 } else if (tp == kVdwPairParType) {
2214 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
2215 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
2216 } else rb_raise(rb_eMolbyError, "invalid member eps");
2217 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
2222 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
2227 up = s_UnionParFromValue(self, &tp, 1);
2228 oldval = s_ParameterRef_GetA14(self);
2229 oldsrc = up->bond.src;
2230 val = rb_Float(val);
2232 if (tp == kVdwParType)
2234 else if (tp == kVdwPairParType)
2236 else rb_raise(rb_eMolbyError, "invalid member A14");
2237 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
2241 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
2246 up = s_UnionParFromValue(self, &tp, 1);
2247 oldval = s_ParameterRef_GetB14(self);
2248 oldsrc = up->bond.src;
2249 val = rb_Float(val);
2251 if (tp == kVdwParType)
2253 else if (tp == kVdwPairParType)
2255 else rb_raise(rb_eMolbyError, "invalid member B14");
2256 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
2261 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
2266 up = s_UnionParFromValue(self, &tp, 1);
2267 oldval = s_ParameterRef_GetReq14(self);
2268 oldsrc = up->bond.src;
2269 val = rb_Float(val);
2271 if (tp == kVdwParType) {
2273 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2274 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2275 } else if (tp == kVdwPairParType) {
2276 up->vdwp.r_eq14 = r;
2277 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2278 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2279 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
2280 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
2284 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
2289 up = s_UnionParFromValue(self, &tp, 1);
2290 oldval = s_ParameterRef_GetEps14(self);
2291 oldsrc = up->bond.src;
2292 val = rb_Float(val);
2293 e = NUM2DBL(val) * KCAL2INTERNAL;
2294 if (tp == kVdwParType) {
2296 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2297 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2298 } else if (tp == kVdwPairParType) {
2300 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2301 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2302 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2303 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2307 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2311 oldval = s_ParameterRef_GetCutoff(self);
2312 oldsrc = up->bond.src;
2313 up = s_UnionParFromValue(self, &tp, 1);
2314 val = rb_Float(val);
2315 if (tp == kVdwCutoffParType) {
2316 up->vdwcutoff.cutoff = NUM2DBL(val);
2317 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2318 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2322 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2326 up = s_UnionParFromValue(self, &tp, 1);
2327 oldval = s_ParameterRef_GetRadius(self);
2328 oldsrc = up->bond.src;
2329 val = rb_Float(val);
2330 if (tp == kElementParType) {
2331 up->atom.radius = NUM2DBL(val);
2332 } else rb_raise(rb_eMolbyError, "invalid member radius");
2333 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2337 static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
2341 up = s_UnionParFromValue(self, &tp, 1);
2342 oldval = s_ParameterRef_GetVdwRadius(self);
2343 oldsrc = up->bond.src;
2344 val = rb_Float(val);
2345 if (tp == kElementParType) {
2346 up->atom.vdw_radius = NUM2DBL(val);
2347 } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
2348 s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
2352 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2355 VALUE *valp, oldval;
2356 up = s_UnionParFromValue(self, &tp, 1);
2357 oldval = s_ParameterRef_GetColor(self);
2358 oldsrc = up->bond.src;
2359 val = rb_ary_to_ary(val);
2360 if (RARRAY_LEN(val) != 3)
2361 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2362 valp = RARRAY_PTR(val);
2363 if (tp == kElementParType) {
2364 up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
2365 up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
2366 up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
2367 } else rb_raise(rb_eMolbyError, "invalid member color");
2368 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2372 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2376 up = s_UnionParFromValue(self, &tp, 1);
2377 oldval = s_ParameterRef_GetAtomicNumber(self);
2378 oldsrc = up->bond.src;
2379 val = rb_Integer(val);
2380 if (tp == kElementParType)
2381 up->atom.number = NUM2INT(val);
2382 else if (tp == kVdwParType) {
2383 up->vdw.atomicNumber = NUM2INT(val);
2384 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2385 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2386 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2390 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2394 up = s_UnionParFromValue(self, &tp, 1);
2395 oldval = s_ParameterRef_GetName(self);
2396 oldsrc = up->bond.src;
2397 if (tp == kElementParType) {
2398 strncpy(up->atom.name, StringValuePtr(val), 4);
2399 } else rb_raise(rb_eMolbyError, "invalid member name");
2400 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2404 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2408 val = rb_Float(val);
2409 oldval = s_ParameterRef_GetWeight(self);
2410 up = s_UnionParFromValue(self, &tp, 1);
2411 oldsrc = up->bond.src;
2412 if (tp == kElementParType)
2413 up->atom.weight = NUM2DBL(val);
2414 else if (tp == kVdwParType)
2415 up->vdw.weight = NUM2DBL(val);
2416 else rb_raise(rb_eMolbyError, "invalid member weight");
2417 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2421 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2425 up = s_UnionParFromValue(self, &tp, 1);
2426 oldval = s_ParameterRef_GetFullName(self);
2427 oldsrc = up->bond.src;
2428 if (tp == kElementParType) {
2429 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2430 up->atom.fullname[15] = 0;
2431 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2432 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2436 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2438 Int tp, com, oldsrc;
2440 up = s_UnionParFromValue(self, &tp, 1);
2441 oldval = s_ParameterRef_GetComment(self);
2442 oldsrc = up->bond.src;
2446 com = ParameterCommentIndex(StringValuePtr(val));
2449 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2453 /* Only false (undefined) and nil (local) can be set */
2454 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2458 up = s_UnionParFromValue(self, &tp, 1);
2459 if (val != Qfalse && val != Qnil)
2460 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2461 oldval = s_ParameterRef_GetSource(self);
2462 oldsrc = up->bond.src;
2463 if (oldsrc != 0 && oldsrc != -1)
2464 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2465 up->bond.src = (val == Qfalse ? -1 : 0);
2466 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2470 static struct s_ParameterAttrDef {
2472 VALUE *symref; /* Address of s_IndexSymbol etc. */
2473 ID id; /* Will be set within InitMolby() */
2474 VALUE (*getter)(VALUE);
2475 VALUE (*setter)(VALUE, VALUE);
2476 } s_ParameterAttrDefTable[] = {
2477 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2478 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2479 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2480 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2481 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2482 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2483 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2484 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2485 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2486 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2487 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2488 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2489 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2490 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2491 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2492 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2493 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2494 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2495 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2496 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2497 {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
2498 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2499 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2500 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2501 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2502 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2503 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2504 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2505 {NULL} /* Sentinel */
2509 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2513 if (TYPE(key) != T_SYMBOL) {
2514 kid = rb_intern(StringValuePtr(key));
2516 } else kid = SYM2ID(key);
2517 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2518 if (s_ParameterAttrDefTable[i].id == kid) {
2519 if (value == Qundef)
2520 return (*(s_ParameterAttrDefTable[i].getter))(self);
2521 else if (s_ParameterAttrDefTable[i].setter == NULL)
2522 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2524 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2527 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2528 return Qnil; /* not reached */
2532 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2534 return s_ParameterRef_SetAttr(self, key, Qundef);
2539 * keys(idx) -> array of valid parameter attributes
2541 * Returns an array of valid parameter attributes (as Symbols).
2544 s_ParameterRef_Keys(VALUE self)
2547 Data_Get_Struct(self, ParameterRef, pref);
2548 switch (pref->parType) {
2550 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2552 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2553 case kDihedralParType:
2554 case kImproperParType:
2555 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2557 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);
2558 case kVdwPairParType:
2559 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2560 case kVdwCutoffParType:
2561 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2562 case kElementParType:
2563 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);
2565 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2567 return Qnil; /* Not reached */
2572 * to_hash(idx) -> Hash
2574 * Returns a hash containing valid parameter names and values
2577 s_ParameterRef_ToHash(VALUE self)
2579 VALUE keys = s_ParameterRef_Keys(self);
2584 retval = rb_hash_new();
2585 for (i = 0; i < RARRAY_LEN(keys); i++) {
2586 VALUE key = RARRAY_PTR(keys)[i];
2587 VALUE val = s_ParameterRef_GetAttr(self, key);
2588 rb_hash_aset(retval, key, val);
2595 * parameter.to_s(idx) -> String
2597 * Returns a string representation of the given parameter
2600 s_ParameterRef_ToString(VALUE self)
2603 char buf[1024], types[4][8];
2604 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2607 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);
2610 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);
2612 case kDihedralParType:
2613 case kImproperParType:
2614 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]));
2616 for (i = 0; i < up->torsion.mult; i++) {
2617 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);
2622 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);
2624 case kVdwPairParType:
2625 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);
2627 case kVdwCutoffParType:
2628 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);
2630 case kElementParType:
2631 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);
2634 return Ruby_NewEncodedStringValue2(buf);
2639 * self == parameterRef -> boolean
2641 * True if the parameters point to the same parameter record.
2644 s_ParameterRef_Equal(VALUE self, VALUE val)
2647 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2648 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2649 } else return Qfalse;
2652 #pragma mark ====== Parameter Class ======
2654 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2655 * is NULL, then the global parameters are looked for. */
2657 /* Rebuild the MD parameter record if necessary: may throw an exception */
2658 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2660 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2663 Data_Get_Struct(val, Molecule, mol);
2665 rb_raise(rb_eMolbyError, "the molecule is empty");
2666 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2667 /* Do self.md_arena.prepare */
2668 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2670 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2675 s_NewParameterValueFromValue(VALUE val)
2678 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2679 Data_Get_Struct(val, Molecule, mol);
2680 s_RebuildMDParameterIfNecessary(val, Qtrue);
2681 MoleculeRetain(mol);
2682 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2685 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2690 s_MoleculeFromParameterValue(VALUE val)
2693 Data_Get_Struct(val, Molecule, mol);
2698 s_ParameterFromParameterValue(VALUE val)
2701 Data_Get_Struct(val, Molecule, mol);
2704 return gBuiltinParameters;
2707 /* Forward declarations */
2708 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2709 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2712 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2714 if (val == rb_cParameter) {
2715 return NULL; /* Parameter class method: builtin parameters */
2716 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2717 return s_MoleculeFromParameterValue(val);
2718 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2719 return s_MoleculeFromParEnumerableValue(val);
2725 * builtin -> Parameter
2727 * Returns a parameter value that points to the global (builtin) parameters.
2728 * Equivalent to Parameter::Builtin (constant).
2731 s_Parameter_Builtin(VALUE self)
2733 static ID s_builtin_id = 0;
2734 if (s_builtin_id == 0)
2735 s_builtin_id = rb_intern("Builtin");
2736 return rb_const_get(rb_cParameter, s_builtin_id);
2741 * bond(idx) -> ParameterRef
2743 * The index-th bond parameter record is returned.
2746 s_Parameter_Bond(VALUE self, VALUE ival)
2750 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2751 idx = NUM2INT(rb_Integer(ival));
2753 n = gBuiltinParameters->nbondPars;
2754 else if (mol->par != NULL)
2755 n = mol->par->nbondPars;
2757 if (idx < -n || idx >= n)
2758 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2761 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2766 * angle(idx) -> ParameterRef
2768 * The index-th angle parameter record is returned.
2771 s_Parameter_Angle(VALUE self, VALUE ival)
2775 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2776 idx = NUM2INT(rb_Integer(ival));
2778 n = gBuiltinParameters->nanglePars;
2779 else if (mol->par != NULL)
2780 n = mol->par->nanglePars;
2782 if (idx < -n || idx >= n)
2783 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2786 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2791 * dihedral(idx) -> ParameterRef
2793 * The index-th dihedral parameter record is returned.
2796 s_Parameter_Dihedral(VALUE self, VALUE ival)
2800 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2801 idx = NUM2INT(rb_Integer(ival));
2803 n = gBuiltinParameters->ndihedralPars;
2804 else if (mol->par != NULL)
2805 n = mol->par->ndihedralPars;
2807 if (idx < -n || idx >= n)
2808 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2811 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2816 * improper(idx) -> ParameterRef
2818 * The index-th improper parameter record is returned.
2821 s_Parameter_Improper(VALUE self, VALUE ival)
2825 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2826 idx = NUM2INT(rb_Integer(ival));
2828 n = gBuiltinParameters->nimproperPars;
2829 else if (mol->par != NULL)
2830 n = mol->par->nimproperPars;
2832 if (idx < -n || idx >= n)
2833 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2836 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2841 * vdw(idx) -> ParameterRef
2843 * The index-th vdw parameter record is returned.
2846 s_Parameter_Vdw(VALUE self, VALUE ival)
2850 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2851 idx = NUM2INT(rb_Integer(ival));
2853 n = gBuiltinParameters->nvdwPars;
2854 else if (mol->par != NULL)
2855 n = mol->par->nvdwPars;
2857 if (idx < -n || idx >= n)
2858 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2861 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2866 * vdw_pair(idx) -> ParameterRef
2868 * The index-th vdw pair parameter record is returned.
2871 s_Parameter_VdwPair(VALUE self, VALUE ival)
2875 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2876 idx = NUM2INT(rb_Integer(ival));
2878 n = gBuiltinParameters->nvdwpPars;
2879 else if (mol->par != NULL)
2880 n = mol->par->nvdwpPars;
2882 if (idx < -n || idx >= n)
2883 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2886 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2891 * vdw_cutoff(idx) -> ParameterRef
2893 * The index-th vdw cutoff parameter record is returned.
2896 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2900 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2901 idx = NUM2INT(rb_Integer(ival));
2903 n = gBuiltinParameters->nvdwCutoffPars;
2904 else if (mol->par != NULL)
2905 n = mol->par->nvdwCutoffPars;
2907 if (idx < -n || idx >= n)
2908 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2911 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2916 * element(idx) -> ParameterRef
2917 * element(t1) -> ParameterRef
2919 * In the first form, the index-th element parameter record is returned. In the second
2920 * form, the element parameter for t1 is looked up (the last index first). t1
2921 * is the element name string (up to 4 characters).
2922 * Unlike other Parameter methods, this is used only for the global parameter.
2925 s_Parameter_Element(VALUE self, VALUE ival)
2928 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2929 int n = gCountElementParameters;
2930 idx1 = NUM2INT(rb_Integer(ival));
2931 if (idx1 < -n || idx1 >= n)
2932 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2935 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2940 strncpy(name, StringValuePtr(ival), 4);
2942 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2943 if (strncmp(ep->name, name, 4) == 0)
2944 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2954 * Returns the number of bond parameters.
2957 s_Parameter_Nbonds(VALUE self)
2960 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2962 n = gBuiltinParameters->nbondPars;
2963 else if (mol->par != NULL)
2964 n = mol->par->nbondPars;
2971 * nangles -> Integer
2973 * Returns the number of angle parameters.
2976 s_Parameter_Nangles(VALUE self)
2979 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2981 n = gBuiltinParameters->nanglePars;
2982 else if (mol->par != NULL)
2983 n = mol->par->nanglePars;
2990 * ndihedrals -> Integer
2992 * Returns the number of dihedral parameters.
2995 s_Parameter_Ndihedrals(VALUE self)
2998 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3000 n = gBuiltinParameters->ndihedralPars;
3001 else if (mol->par != NULL)
3002 n = mol->par->ndihedralPars;
3009 * nimpropers -> Integer
3011 * Returns the number of improper parameters.
3014 s_Parameter_Nimpropers(VALUE self)
3017 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3019 n = gBuiltinParameters->nimproperPars;
3020 else if (mol->par != NULL)
3021 n = mol->par->nimproperPars;
3030 * Returns the number of vdw parameters.
3033 s_Parameter_Nvdws(VALUE self)
3036 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3038 n = gBuiltinParameters->nvdwPars;
3039 else if (mol->par != NULL)
3040 n = mol->par->nvdwPars;
3047 * nvdw_pairs -> Integer
3049 * Returns the number of vdw pair parameters.
3052 s_Parameter_NvdwPairs(VALUE self)
3055 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3057 n = gBuiltinParameters->nvdwpPars;
3058 else if (mol->par != NULL)
3059 n = mol->par->nvdwpPars;
3066 * nvdw_cutoffs -> Integer
3068 * Returns the number of vdw cutoff parameters.
3071 s_Parameter_NvdwCutoffs(VALUE self)
3074 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3076 n = gBuiltinParameters->nvdwCutoffPars;
3077 else if (mol->par != NULL)
3078 n = mol->par->nvdwCutoffPars;
3085 * nelements -> Integer
3087 * Returns the number of element parameters.
3090 s_Parameter_Nelements(VALUE self)
3092 return INT2NUM(gCountElementParameters);
3097 * bonds -> ParEnumerable
3099 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
3100 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
3101 * useful when all accessible parameters should be examined by use of 'each' method.
3104 s_Parameter_Bonds(VALUE self)
3106 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3107 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
3112 * angles -> ParEnumerable
3114 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
3115 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
3116 * useful when all accessible parameters should be examined by use of 'each' method.
3119 s_Parameter_Angles(VALUE self)
3121 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3122 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
3127 * dihedrals -> ParEnumerable
3129 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
3130 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
3131 * useful when all accessible parameters should be examined by use of 'each' method.
3134 s_Parameter_Dihedrals(VALUE self)
3136 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3137 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
3142 * impropers -> ParEnumerable
3144 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
3145 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
3146 * useful when all accessible parameters should be examined by use of 'each' method.
3149 s_Parameter_Impropers(VALUE self)
3151 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3152 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
3157 * vdws -> ParEnumerable
3159 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
3160 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
3161 * useful when all accessible parameters should be examined by use of 'each' method.
3164 s_Parameter_Vdws(VALUE self)
3166 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3167 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
3172 * vdw_pairs -> ParEnumerable
3174 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
3175 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
3176 * useful when all accessible parameters should be examined by use of 'each' method.
3179 s_Parameter_VdwPairs(VALUE self)
3181 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3182 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
3187 * vdw_cutoffs -> ParEnumerable
3189 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
3190 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
3191 * useful when all accessible parameters should be examined by use of 'each' method.
3194 s_Parameter_VdwCutoffs(VALUE self)
3196 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3197 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
3202 * elements -> ParEnumerable
3204 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
3205 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
3206 * useful when all accessible parameters should be examined by use of 'each' method.
3209 s_Parameter_Elements(VALUE self)
3211 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3212 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
3216 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
3218 VALUE atval, optval;
3221 int i, n, idx, flags, is_global;
3223 rb_scan_args(argc, argv, "1*", &atval, &optval);
3225 /* Get the atom types */
3227 case kBondParType: n = 2; break;
3228 case kAngleParType: n = 3; break;
3229 case kDihedralParType: n = 4; break;
3230 case kImproperParType: n = 4; break;
3231 case kVdwParType: n = 1; break;
3232 case kVdwPairParType: n = 2; break;
3233 default: return Qnil;
3235 s_ScanAtomTypes(atval, n, t);
3236 for (i = 0; i < n; i++) {
3237 if (t[i] < kAtomTypeMinimum) {
3238 /* Explicit atom index */
3240 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
3241 if (t[i] >= mol->natoms)
3242 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
3244 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
3248 /* Analyze options */
3250 n = RARRAY_LEN(optval);
3251 for (i = 0; i < n; i++) {
3252 VALUE oval = RARRAY_PTR(optval)[i];
3253 if (oval == ID2SYM(rb_intern("global")))
3254 flags |= kParameterLookupGlobal;
3255 else if (oval == ID2SYM(rb_intern("local")))
3256 flags |= kParameterLookupLocal;
3257 else if (oval == ID2SYM(rb_intern("missing")))
3258 flags |= kParameterLookupMissing;
3259 else if (oval == ID2SYM(rb_intern("nowildcard")))
3260 flags |= kParameterLookupNoWildcard;
3261 else if (oval == ID2SYM(rb_intern("nobasetype")))
3262 flags |= kParameterLookupNoBaseAtomType;
3263 else if (oval == ID2SYM(rb_intern("create")))
3267 flags = kParameterLookupGlobal | kParameterLookupLocal;
3272 case kBondParType: {
3275 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3277 idx = bp - mol->par->bondPars;
3281 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3283 idx = bp - gBuiltinParameters->bondPars;
3288 case kAngleParType: {
3291 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
3293 idx = ap - mol->par->anglePars;
3297 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
3299 idx = ap - gBuiltinParameters->anglePars;
3304 case kDihedralParType: {
3307 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3309 idx = tp - mol->par->dihedralPars;
3313 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3315 idx = tp - gBuiltinParameters->dihedralPars;
3320 case kImproperParType: {
3323 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3325 idx = tp - mol->par->improperPars;
3329 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3331 idx = tp - gBuiltinParameters->improperPars;
3339 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3341 idx = vp - mol->par->vdwPars;
3345 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3347 idx = vp - gBuiltinParameters->vdwPars;
3352 case kVdwPairParType: {
3355 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3357 idx = vp - mol->par->vdwpPars;
3361 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3363 idx = vp - gBuiltinParameters->vdwpPars;
3372 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3375 /* Insert a new parameter record */
3377 Int count = ParameterGetCountForType(mol->par, parType);
3378 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3379 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3380 IntGroupRelease(ig);
3383 /* Set atom types */
3384 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3389 up->bond.type1 = t[0];
3390 up->bond.type2 = t[1];
3393 up->angle.type1 = t[0];
3394 up->angle.type2 = t[1];
3395 up->angle.type3 = t[2];
3397 case kDihedralParType:
3398 case kImproperParType:
3399 up->torsion.type1 = t[0];
3400 up->torsion.type2 = t[1];
3401 up->torsion.type3 = t[2];
3402 up->torsion.type4 = t[3];
3405 up->vdw.type1 = t[0];
3407 case kVdwPairParType:
3408 up->vdwp.type1 = t[0];
3409 up->vdwp.type2 = t[1];
3416 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3421 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3422 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3424 * Find the parameter record that matches the given atom types. The atom types are given
3425 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3426 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3427 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3428 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3431 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3434 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3436 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3437 parType = s_ParTypeFromValue(argv[0]);
3438 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3443 * self == parameter -> boolean
3445 * True if the parameters point to the same parameter table.
3448 s_Parameter_Equal(VALUE self, VALUE val)
3450 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3451 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3452 } else return Qfalse;
3455 #pragma mark ====== ParEnumerable Class ======
3457 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3458 and the parameter type. If the Molecule is NULL, then it refers to the
3459 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3460 the global parameters are always accessible. */
3462 typedef struct ParEnumerable {
3464 Int parType; /* Same as parType in ParameterRef */
3467 static ParEnumerable *
3468 s_ParEnumerableNew(Molecule *mol, Int parType)
3470 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3474 MoleculeRetain(mol);
3475 pen->parType = parType;
3481 s_ParEnumerableRelease(ParEnumerable *pen)
3484 if (pen->mol != NULL)
3485 MoleculeRelease(pen->mol);
3491 s_MoleculeFromParEnumerableValue(VALUE val)
3494 Data_Get_Struct(val, ParEnumerable, pen);
3499 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3501 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3503 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3504 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3509 * par_type -> String
3511 * Get the parameter type, like "bond", "angle", etc.
3514 s_ParEnumerable_ParType(VALUE self) {
3517 Data_Get_Struct(self, ParEnumerable, pen);
3519 if (tp == kElementParType)
3520 return Ruby_NewEncodedStringValue2("element");
3521 tp -= kFirstParType;
3522 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3523 return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
3524 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3529 * self[idx] -> ParameterRef
3531 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3532 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3533 * parent Parameter object of self.
3535 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3536 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3539 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3542 Data_Get_Struct(self, ParEnumerable, pen);
3543 switch (pen->parType) {
3544 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3545 case kBondParType: return s_Parameter_Bond(self, ival);
3546 case kAngleParType: return s_Parameter_Angle(self, ival);
3547 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3548 case kImproperParType: return s_Parameter_Improper(self, ival);
3549 case kVdwParType: return s_Parameter_Vdw(self, ival);
3550 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3551 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3552 case kElementParType: return s_Parameter_Element(self, ival);
3554 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3556 return Qnil; /* Not reached */
3563 * Returns the number of parameters included in this enumerable.
3566 s_ParEnumerable_Length(VALUE self)
3569 Data_Get_Struct(self, ParEnumerable, pen);
3570 switch (pen->parType) {
3571 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3572 case kBondParType: return s_Parameter_Nbonds(self);
3573 case kAngleParType: return s_Parameter_Nangles(self);
3574 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3575 case kImproperParType: return s_Parameter_Nimpropers(self);
3576 case kVdwParType: return s_Parameter_Nvdws(self);
3577 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3578 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3579 case kElementParType: return s_Parameter_Nelements(self);
3581 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3583 return Qnil; /* Not reached */
3590 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3593 s_ParEnumerable_Each(VALUE self)
3599 Data_Get_Struct(self, ParEnumerable, pen);
3600 if (pen->parType == kElementParType)
3601 n = gCountElementParameters;
3603 switch (pen->parType) {
3604 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3605 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3606 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3607 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3608 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3609 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3610 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3612 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3614 if (pen->mol == NULL)
3615 n = *((Int *)((char *)gBuiltinParameters + ofs));
3616 else if (pen->mol->par != NULL)
3617 n = *((Int *)((char *)(pen->mol->par) + ofs));
3620 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3621 Data_Get_Struct(aval, ParameterRef, pref);
3622 for (i = 0; i < n; i++) {
3631 * reverse_each {|pref| ...}
3633 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3636 s_ParEnumerable_ReverseEach(VALUE self)
3642 Data_Get_Struct(self, ParEnumerable, pen);
3643 if (pen->parType == kElementParType)
3644 n = gCountElementParameters;
3646 switch (pen->parType) {
3647 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3648 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3649 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3650 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3651 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3652 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3653 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3655 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3657 if (pen->mol == NULL)
3658 n = *((Int *)((char *)gBuiltinParameters + ofs));
3659 else if (pen->mol->par != NULL)
3660 n = *((Int *)((char *)(pen->mol->par) + ofs));
3663 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3664 Data_Get_Struct(aval, ParameterRef, pref);
3665 for (i = n - 1; i >= 0; i--) {
3674 * insert(idx = nil, pref = nil) -> ParameterRef
3676 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3677 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3678 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3679 * parameter is left undefined.
3680 * Throws an exception if ParEnumerable points to the global parameter.
3683 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3691 Data_Get_Struct(self, ParEnumerable, pen);
3692 if (pen->mol == NULL)
3693 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3694 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3695 rb_scan_args(argc, argv, "02", &ival, &pval);
3697 i = NUM2INT(rb_Integer(ival));
3699 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3704 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3705 if (up == NULL || type != pen->parType)
3706 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3707 ParameterCopyOneWithType(&u, up, pen->parType);
3710 memset(&u, 0, sizeof(u));
3713 ig = IntGroupNewWithPoints(n, 1, -1);
3714 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3716 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3717 MolActionCallback_registerUndo(pen->mol, act);
3718 MolActionRelease(act);
3720 IntGroupRelease(ig);
3721 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3729 * Delete the parameter(s) specified by the argument.
3732 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3737 Data_Get_Struct(self, ParEnumerable, pen);
3738 if (pen->mol == NULL)
3739 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3740 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3741 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3742 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3745 ig = IntGroupFromValue(ival);
3746 if ((i = IntGroupGetCount(ig)) == 0) {
3747 IntGroupRelease(ig);
3751 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3752 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3755 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3756 IntGroupRelease(ig);
3762 * lookup(atom_types, options, ...) -> ParameterRef
3763 * lookup(atom_type_string, options, ...) -> ParameterRef
3765 * Find the parameter record that matches the given atom types. The arguments are
3766 * the same as Parameter#lookup, except for the parameter type which is implicitly
3770 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3773 Data_Get_Struct(self, ParEnumerable, pen);
3774 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3779 * self == parEnumerable -> boolean
3781 * True if the arguments point to the same parameter table and type.
3784 s_ParEnumerable_Equal(VALUE self, VALUE val)
3786 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3787 ParEnumerable *pen1, *pen2;
3788 Data_Get_Struct(self, ParEnumerable, pen1);
3789 Data_Get_Struct(val, ParEnumerable, pen2);
3790 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3791 } else return Qfalse;
3794 #pragma mark ====== AtomRef Class ======
3796 /* Forward declaration for register undo */
3797 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3799 /* Ruby string "set_atom_attr" */
3800 static VALUE s_SetAtomAttrString;
3803 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3807 Data_Get_Struct(self, AtomRef, aref);
3808 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3809 if (idx < 0 || idx >= aref->mol->natoms)
3810 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3812 *app = aref->mol->atoms + idx;
3819 s_AtomFromValue(VALUE self)
3822 s_AtomIndexFromValue(self, &ap, NULL);
3827 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3830 s_AtomIndexFromValue(self, &ap, mpp);
3835 s_NotifyModificationForAtomRef(VALUE self)
3838 Data_Get_Struct(self, AtomRef, aref);
3839 MoleculeIncrementModifyCount(aref->mol);
3843 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3846 Data_Get_Struct(self, AtomRef, aref);
3847 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3850 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3851 MolActionCallback_registerUndo(aref->mol, act);
3852 MoleculeCallback_notifyModification(aref->mol, 0);
3853 /* Request MD rebuilt if necessary */
3854 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3855 aref->mol->needsMDRebuild = 1;
3860 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3863 aref = AtomRefNew(mol, idx);
3864 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3868 s_AtomRef_GetMolecule(VALUE self)
3871 s_AtomIndexFromValue(self, NULL, &mpp);
3872 return ValueFromMolecule(mpp);
3875 static VALUE s_AtomRef_GetIndex(VALUE self) {
3876 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3879 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3880 return INT2NUM(s_AtomFromValue(self)->segSeq);
3883 static VALUE s_AtomRef_GetSegName(VALUE self) {
3884 char *p = s_AtomFromValue(self)->segName;
3885 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3888 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3889 return INT2NUM(s_AtomFromValue(self)->resSeq);
3892 static VALUE s_AtomRef_GetResName(VALUE self) {
3893 char *p = s_AtomFromValue(self)->resName;
3894 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3897 static VALUE s_AtomRef_GetName(VALUE self) {
3898 char *p = s_AtomFromValue(self)->aname;
3899 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3902 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3903 int type = s_AtomFromValue(self)->type;
3904 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3905 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
3908 static VALUE s_AtomRef_GetCharge(VALUE self) {
3909 return rb_float_new(s_AtomFromValue(self)->charge);
3912 static VALUE s_AtomRef_GetWeight(VALUE self) {
3913 return rb_float_new(s_AtomFromValue(self)->weight);
3916 static VALUE s_AtomRef_GetElement(VALUE self) {
3917 char *p = s_AtomFromValue(self)->element;
3918 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
3921 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3922 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3925 static VALUE s_AtomRef_GetConnects(VALUE self) {
3928 Atom *ap = s_AtomFromValue(self);
3929 retval = rb_ary_new();
3930 cp = AtomConnectData(&ap->connect);
3931 for (i = 0; i < ap->connect.count; i++)
3932 rb_ary_push(retval, INT2NUM(cp[i]));
3936 static VALUE s_AtomRef_GetR(VALUE self) {
3937 return ValueFromVector(&(s_AtomFromValue(self)->r));
3940 static VALUE s_AtomRef_GetX(VALUE self) {
3941 return rb_float_new(s_AtomFromValue(self)->r.x);
3944 static VALUE s_AtomRef_GetY(VALUE self) {
3945 return rb_float_new(s_AtomFromValue(self)->r.y);
3948 static VALUE s_AtomRef_GetZ(VALUE self) {
3949 return rb_float_new(s_AtomFromValue(self)->r.z);
3952 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3956 s_AtomIndexFromValue(self, &ap, &mp);
3958 if (mp->cell != NULL)
3959 TransformVec(&r1, mp->cell->rtr, &r1);
3963 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3964 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3965 return ValueFromVector(&r1);
3968 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3969 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3972 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3973 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3976 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3977 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3980 static VALUE s_AtomRef_GetSigma(VALUE self) {
3981 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3984 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3985 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3988 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3989 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3992 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3993 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3996 static VALUE s_AtomRef_GetV(VALUE self) {
3997 return ValueFromVector(&(s_AtomFromValue(self)->v));
4000 static VALUE s_AtomRef_GetF(VALUE self) {
4001 Vector v = s_AtomFromValue(self)->f;
4002 VecScaleSelf(v, INTERNAL2KCAL);
4003 return ValueFromVector(&v);
4006 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
4007 return rb_float_new(s_AtomFromValue(self)->occupancy);
4010 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
4011 return rb_float_new(s_AtomFromValue(self)->tempFactor);
4014 static VALUE s_AtomRef_GetAniso(VALUE self) {
4017 Atom *ap = s_AtomFromValue(self);
4018 if (ap->aniso == NULL)
4020 retval = rb_ary_new();
4021 for (i = 0; i < 6; i++)
4022 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
4023 if (ap->aniso->has_bsig) {
4024 rb_ary_push(retval, INT2NUM(0));
4025 for (i = 0; i < 6; i++)
4026 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
4031 static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
4034 Atom *ap = s_AtomFromValue(self);
4035 if (ap->aniso == NULL)
4037 retval = rb_ary_new();
4038 for (i = 0; i < 3; i++)
4039 rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
4043 static VALUE s_AtomRef_GetSymop(VALUE self) {
4045 Atom *ap = s_AtomFromValue(self);
4046 if (!ap->symop.alive)
4048 retval = rb_ary_new();
4049 rb_ary_push(retval, INT2NUM(ap->symop.sym));
4050 rb_ary_push(retval, INT2NUM(ap->symop.dx));
4051 rb_ary_push(retval, INT2NUM(ap->symop.dy));
4052 rb_ary_push(retval, INT2NUM(ap->symop.dz));
4053 rb_ary_push(retval, INT2NUM(ap->symbase));
4057 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
4058 return INT2NUM(s_AtomFromValue(self)->intCharge);
4061 static VALUE s_AtomRef_GetFixForce(VALUE self) {
4062 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
4065 static VALUE s_AtomRef_GetFixPos(VALUE self) {
4066 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
4069 static VALUE s_AtomRef_GetExclusion(VALUE self) {
4073 MDExclusion *exinfo;
4076 idx = s_AtomIndexFromValue(self, &ap, &mol);
4077 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
4078 VALUE mval = ValueFromMolecule(mol);
4079 s_RebuildMDParameterIfNecessary(mval, Qnil);
4081 if (mol->arena->exinfo == NULL)
4083 exinfo = mol->arena->exinfo + idx;
4084 exlist = mol->arena->exlist;
4085 retval = rb_ary_new();
4086 aval = rb_ary_new();
4087 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
4088 rb_ary_push(aval, INT2NUM(exlist[i]));
4089 rb_ary_push(retval, aval);
4090 aval = rb_ary_new();
4091 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
4092 rb_ary_push(aval, INT2NUM(exlist[i]));
4093 rb_ary_push(retval, aval);
4094 aval = rb_ary_new();
4095 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
4096 rb_ary_push(aval, INT2NUM(exlist[i]));
4097 rb_ary_push(retval, aval);
4101 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
4102 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
4105 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
4106 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
4109 static VALUE s_AtomRef_GetHidden(VALUE self) {
4110 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4113 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
4116 Atom *ap = s_AtomFromValue(self);
4117 if (ap->anchor == NULL)
4119 count = ap->anchor->connect.count;
4120 retval = rb_ary_new2(count * 2);
4121 cp = AtomConnectData(&ap->anchor->connect);
4122 for (i = 0; i < count; i++) {
4123 rb_ary_store(retval, i, INT2NUM(cp[i]));
4124 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
4129 static VALUE s_AtomRef_GetUFFType(VALUE self) {
4130 char *p = s_AtomFromValue(self)->uff_type;
4131 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
4134 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
4135 rb_raise(rb_eMolbyError, "index cannot be directly set");
4139 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
4140 VALUE oval = s_AtomRef_GetSegSeq(self);
4141 val = rb_Integer(val);
4142 s_AtomFromValue(self)->segSeq = NUM2INT(val);
4143 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
4147 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
4148 char *p = StringValuePtr(val);
4149 VALUE oval = s_AtomRef_GetSegName(self);
4150 strncpy(s_AtomFromValue(self)->segName, p, 4);
4151 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
4155 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
4156 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
4157 return val; /* Not reached */
4160 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
4161 Atom *ap = s_AtomFromValue(self);
4162 char *p = StringValuePtr(val);
4163 VALUE oval = s_AtomRef_GetName(self);
4164 if (ap->anchor != NULL && p[0] == '_')
4165 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4166 strncpy(ap->aname, p, 4);
4167 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
4171 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
4173 char *p = StringValuePtr(val);
4174 VALUE oval = s_AtomRef_GetAtomType(self);
4175 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
4176 if (type != 0 && type < kAtomTypeMinimum)
4177 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
4178 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
4179 mp->needsMDRebuild = 1;
4180 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
4184 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
4186 VALUE oval = s_AtomRef_GetCharge(self);
4187 val = rb_Float(val);
4188 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
4189 mp->needsMDRebuild = 1;
4190 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
4194 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
4196 VALUE oval = s_AtomRef_GetWeight(self);
4197 val = rb_Float(val);
4198 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
4199 mp->needsMDRebuild = 1;
4200 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
4204 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
4207 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4208 char *p = StringValuePtr(val);
4209 VALUE oval = s_AtomRef_GetElement(self);
4210 ap->atomicNumber = ElementToInt(p);
4211 ElementToString(ap->atomicNumber, ap->element);
4212 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4214 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
4215 mp->needsMDRebuild = 1;
4219 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
4222 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
4223 VALUE oval = s_AtomRef_GetAtomicNumber(self);
4224 val = rb_Integer(val);
4225 ap->atomicNumber = NUM2INT(val);
4226 ElementToString(ap->atomicNumber, ap->element);
4227 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
4229 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
4230 mp->needsMDRebuild = 1;
4234 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
4235 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
4236 return val; /* Not reached */
4239 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
4242 VALUE oval = s_AtomRef_GetR(self);
4243 VectorFromValue(val, &v);
4244 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
4245 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
4246 mp->needsMDCopyCoordinates = 1;
4250 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
4253 VALUE oval = s_AtomRef_GetX(self);
4254 val = rb_Float(val);
4256 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
4257 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
4258 mp->needsMDCopyCoordinates = 1;
4262 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
4265 VALUE oval = s_AtomRef_GetY(self);
4266 val = rb_Float(val);
4268 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
4269 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
4270 mp->needsMDCopyCoordinates = 1;
4274 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
4277 VALUE oval = s_AtomRef_GetZ(self);
4278 val = rb_Float(val);
4280 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
4281 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
4282 mp->needsMDCopyCoordinates = 1;
4286 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
4290 s_AtomIndexFromValue(self, &ap, &mp);
4292 VectorFromValue(val, &v);
4293 if (mp->cell != NULL)
4294 TransformVec(&v, mp->cell->tr, &v);
4296 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4297 mp->needsMDCopyCoordinates = 1;
4301 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
4306 s_AtomIndexFromValue(self, &ap, &mp);
4308 val = rb_Float(val);
4310 if (mp->cell != NULL) {
4311 TransformVec(&v, mp->cell->rtr, &v);
4313 TransformVec(&v, mp->cell->tr, &v);
4316 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4317 mp->needsMDCopyCoordinates = 1;
4321 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4326 s_AtomIndexFromValue(self, &ap, &mp);
4328 val = rb_Float(val);
4330 if (mp->cell != NULL) {
4331 TransformVec(&v, mp->cell->rtr, &v);
4333 TransformVec(&v, mp->cell->tr, &v);
4336 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4337 mp->needsMDCopyCoordinates = 1;
4341 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4346 s_AtomIndexFromValue(self, &ap, &mp);
4348 val = rb_Float(val);
4350 if (mp->cell != NULL) {
4351 TransformVec(&v, mp->cell->rtr, &v);
4353 TransformVec(&v, mp->cell->tr, &v);
4356 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4357 mp->needsMDCopyCoordinates = 1;
4361 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4364 VALUE oval = s_AtomRef_GetSigma(self);
4365 VectorFromValue(val, &v);
4366 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4367 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4368 mp->needsMDCopyCoordinates = 1;
4372 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4375 VALUE oval = s_AtomRef_GetSigmaX(self);
4376 val = rb_Float(val);
4378 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4379 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4380 mp->needsMDCopyCoordinates = 1;
4384 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4387 VALUE oval = s_AtomRef_GetSigmaY(self);
4388 val = rb_Float(val);
4390 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4391 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4392 mp->needsMDCopyCoordinates = 1;
4396 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4399 VALUE oval = s_AtomRef_GetSigmaZ(self);
4400 val = rb_Float(val);
4402 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4403 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4404 mp->needsMDCopyCoordinates = 1;
4408 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4412 VALUE oval = s_AtomRef_GetV(self);
4413 VectorFromValue(val, &v);
4414 s_AtomIndexFromValue(self, &ap, &mp);
4416 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4417 mp->needsMDCopyCoordinates = 1;
4421 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4424 VALUE oval = s_AtomRef_GetF(self);
4425 VectorFromValue(val, &v);
4426 VecScaleSelf(v, KCAL2INTERNAL);
4427 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4428 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4429 mp->needsMDCopyCoordinates = 1;
4433 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4434 VALUE oval = s_AtomRef_GetOccupancy(self);
4436 val = rb_Float(val);
4437 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4438 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4439 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4443 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4444 VALUE oval = s_AtomRef_GetTempFactor(self);
4445 val = rb_Float(val);
4446 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4447 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4451 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4456 VALUE oval = s_AtomRef_GetAniso(self);
4457 Data_Get_Struct(self, AtomRef, aref);
4458 val = rb_funcall(val, rb_intern("to_a"), 0);
4459 n = RARRAY_LEN(val);
4460 valp = RARRAY_PTR(val);
4461 for (i = 0; i < 6; i++) {
4463 f[i] = NUM2DBL(rb_Float(valp[i]));
4467 type = NUM2INT(rb_Integer(valp[6]));
4470 for (i = 0; i < 6; i++)
4471 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4473 for (i = 0; i < 6; i++)
4476 i = s_AtomIndexFromValue(self, NULL, NULL);
4477 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4478 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4482 static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
4483 rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
4484 return val; /* Not reached */
4487 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4493 VALUE oval = s_AtomRef_GetSymop(self);
4494 i = s_AtomIndexFromValue(self, &ap, &mol);
4496 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4498 val = rb_funcall(val, rb_intern("to_a"), 0);
4499 n = RARRAY_LEN(val);
4500 valp = RARRAY_PTR(val);
4501 for (i = 0; i < 5; i++) {
4503 if (valp[i] == Qnil)
4506 ival[i] = NUM2INT(rb_Integer(valp[i]));
4507 } else ival[i] = -100000;
4510 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4511 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));
4512 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4513 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4514 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4515 return val; /* No need to change */
4516 if (ival[0] != -100000)
4517 ap->symop.sym = ival[0];
4518 if (ival[1] != -100000)
4519 ap->symop.dx = ival[1];
4520 if (ival[2] != -100000)
4521 ap->symop.dy = ival[2];
4522 if (ival[3] != -100000)
4523 ap->symop.dz = ival[3];
4524 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4525 if (ival[4] != -100000)
4526 ap->symbase = ival[4];
4527 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4528 /* The anisotropic parameters should be recalculated */
4529 VALUE oaval = s_AtomRef_GetAniso(self);
4530 MoleculeSetAnisoBySymop(mol, i);
4531 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4533 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4537 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4538 VALUE oval = s_AtomRef_GetIntCharge(self);
4539 val = rb_Integer(val);
4540 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4541 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4545 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4547 VALUE oval = s_AtomRef_GetFixForce(self);
4548 val = rb_Float(val);
4549 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4550 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4551 mp->needsMDRebuild = 1;
4555 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4558 VALUE oval = s_AtomRef_GetFixPos(self);
4559 VectorFromValue(val, &v);
4560 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4561 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4562 mp->needsMDRebuild = 1;
4566 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4567 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4568 return val; /* Not reached */
4571 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4572 VALUE oval = s_AtomRef_GetIntCharge(self);
4573 val = rb_Integer(val);
4574 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4575 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4579 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4580 VALUE oval = s_AtomRef_GetIntCharge(self);
4581 val = rb_Integer(val);
4582 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4583 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4587 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4588 Atom *ap = s_AtomFromValue(self);
4589 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4591 ap->exflags |= kAtomHiddenFlag;
4593 ap->exflags &= ~kAtomHiddenFlag;
4595 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4599 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4600 Int idx, i, j, k, n, *ip;
4607 MolAction **undoActions;
4608 memset(&ac, 0, sizeof(ac));
4609 idx = s_AtomIndexFromValue(self, &ap, &mol);
4610 oval = s_AtomRef_GetAnchorList(self);
4612 val = rb_ary_to_ary(val);
4613 n = RARRAY_LEN(val);
4616 if (ap->anchor != NULL) {
4617 AtomConnectResize(&ap->anchor->connect, 0);
4618 free(ap->anchor->coeffs);
4621 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4626 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4627 if (ap->aname[0] == '_')
4628 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4629 ip = (Int *)malloc(sizeof(Int) * n);
4631 for (i = 0; i < n; i++) {
4632 v = RARRAY_PTR(val)[i];
4633 if (rb_obj_is_kind_of(v, rb_cFloat))
4635 j = NUM2INT(rb_Integer(v));
4636 if (j < 0 || j >= mol->natoms)
4637 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4638 for (k = 0; k < i; k++) {
4640 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4646 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4647 else if (i * 2 != n)
4648 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4649 dp = (Double *)malloc(sizeof(Double) * n / 2);
4650 for (i = 0; i < n / 2; i++) {
4651 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4653 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4659 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4663 rb_raise(rb_eMolbyError, "invalid argument");
4664 if (nUndoActions > 0) {
4665 for (i = 0; i < nUndoActions; i++) {
4666 MolActionCallback_registerUndo(mol, undoActions[i]);
4667 MolActionRelease(undoActions[i]);
4671 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4675 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4676 Atom *ap = s_AtomFromValue(self);
4677 char *p = StringValuePtr(val);
4678 VALUE oval = s_AtomRef_GetUFFType(self);
4679 strncpy(ap->uff_type, p, 5);
4680 ap->uff_type[5] = 0;
4681 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4685 static struct s_AtomAttrDef {
4687 VALUE *symref; /* Address of s_IndexSymbol etc. */
4688 ID id; /* Will be set within InitMolby() */
4689 VALUE (*getter)(VALUE);
4690 VALUE (*setter)(VALUE, VALUE);
4691 } s_AtomAttrDefTable[] = {
4692 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4693 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4694 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4695 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4696 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4697 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4698 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4699 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4700 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4701 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4702 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4703 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4704 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4705 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4706 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4707 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4708 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4709 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4710 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4711 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4712 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4713 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4714 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4715 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4716 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4717 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4718 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4719 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4720 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4721 {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues, s_AtomRef_SetAnisoEigenValues},
4722 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4723 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4724 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4725 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4726 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4727 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4728 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4729 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4730 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4731 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4732 {NULL} /* Sentinel */
4736 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4740 if (TYPE(key) != T_SYMBOL) {
4741 kid = rb_intern(StringValuePtr(key));
4743 } else kid = SYM2ID(key);
4744 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4745 if (s_AtomAttrDefTable[i].id == kid) {
4746 if (value == Qundef)
4747 return (*(s_AtomAttrDefTable[i].getter))(self);
4749 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4752 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4753 return Qnil; /* not reached */
4757 s_AtomRef_GetAttr(VALUE self, VALUE key)
4759 return s_AtomRef_SetAttr(self, key, Qundef);
4764 * self == atomRef -> boolean
4766 * True if the two references point to the same atom.
4769 s_AtomRef_Equal(VALUE self, VALUE val)
4771 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4772 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4773 } else return Qfalse;
4776 #pragma mark ====== MolEnumerable Class ======
4778 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4782 * self[idx] -> AtomRef or Array of Integers
4784 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4785 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4786 * value is a String. Otherwise, the return value is an Array of Integers.
4789 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4791 MolEnumerable *mseq;
4794 Data_Get_Struct(self, MolEnumerable, mseq);
4796 if (mseq->kind == kAtomKind) {
4797 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4799 idx1 = NUM2INT(arg1);
4800 switch (mseq->kind) {
4802 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4803 if (idx2 < 0 || idx2 >= mol->nbonds)
4804 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4805 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4808 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4809 if (idx2 < 0 || idx2 >= mol->nangles)
4810 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4811 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4813 case kDihedralKind: {
4814 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4815 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4816 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4817 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]));
4819 case kImproperKind: {
4820 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4821 if (idx2 < 0 || idx2 >= mol->nimpropers)
4822 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4823 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]));
4825 case kResidueKind: {
4827 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4828 if (idx2 < 0 || idx2 >= mol->nresidues)
4829 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4830 p = mol->residues[idx2];
4831 return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
4841 * Returns the number of objects included in this enumerable.
4844 s_MolEnumerable_Length(VALUE self)
4846 MolEnumerable *mseq;
4847 Data_Get_Struct(self, MolEnumerable, mseq);
4848 switch (mseq->kind) {
4850 return INT2NUM(mseq->mol->natoms);
4852 return INT2NUM(mseq->mol->nbonds);
4854 return INT2NUM(mseq->mol->nangles);
4856 return INT2NUM(mseq->mol->ndihedrals);
4858 return INT2NUM(mseq->mol->nimpropers);
4860 return INT2NUM(mseq->mol->nresidues);
4869 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4870 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4871 * For the atoms, a same AtomRef object is passed (with different internal information)
4872 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4873 * for each iteration.
4876 s_MolEnumerable_Each(VALUE self)
4878 MolEnumerable *mseq;
4880 int len = NUM2INT(s_MolEnumerable_Length(self));
4881 Data_Get_Struct(self, MolEnumerable, mseq);
4882 if (mseq->kind == kAtomKind) {
4883 /* The same AtomRef object will be used during the loop */
4884 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4885 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4886 for (i = 0; i < len; i++) {
4891 /* A new ruby object will be created at each iteration (not very efficient) */
4892 for (i = 0; i < len; i++) {
4893 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4901 * self == molEnumerable -> boolean
4903 * True if the two arguments point to the same molecule and enumerable type.
4906 s_MolEnumerable_Equal(VALUE self, VALUE val)
4908 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4909 MolEnumerable *mseq1, *mseq2;
4910 Data_Get_Struct(self, MolEnumerable, mseq1);
4911 Data_Get_Struct(val, MolEnumerable, mseq2);
4912 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4913 } else return Qfalse;
4917 #pragma mark ====== Molecule Class ======
4919 #pragma mark ------ Allocate/Release/Accessor ------
4921 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
4922 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4923 char *gLoadSaveErrorMessage = NULL;
4925 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4928 MoleculeFromValue(VALUE val)
4931 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4932 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4933 Data_Get_Struct(val, Molecule, mol);
4937 static VALUE sMoleculeRetainArray = Qnil;
4939 /* The function is called from MoleculeRelease() */
4940 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4941 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4942 /* object is always returned for the same Molecule. */
4943 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4944 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4945 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4946 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4947 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4949 /* Register/unregister the exmolobj Ruby object */
4951 MoleculeReleaseExternalObj(Molecule *mol)
4953 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4954 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4955 mol->exmolobjProtected = 0;
4960 MoleculeRetainExternalObj(Molecule *mol)
4962 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4963 if (sMoleculeRetainArray == Qnil) {
4964 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4965 sMoleculeRetainArray = rb_ary_new();
4968 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4969 mol->exmolobjProtected = 1;
4973 /* Release hook function for Ruby */
4975 MoleculeReleaseHook(Molecule *mol)
4977 if (mol->exmolobj != NULL) {
4978 /* No need to remove from sMoleculeRetainArray */
4979 mol->exmolobj = NULL;
4980 mol->exmolobjProtected = 0;
4982 MoleculeRelease(mol);
4986 ValueFromMolecule(Molecule *mol)
4990 if (mol->exmolobj != NULL)
4991 return (VALUE)mol->exmolobj;
4992 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4993 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4994 return (VALUE)mol->exmolobj;
4999 s_Molecule_Alloc(VALUE klass)
5002 Molecule *mol = MoleculeNew();
5003 val = ValueFromMolecule(mol);
5004 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
5009 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
5013 if (FIXNUM_P(val)) {
5015 if (n >= 0 && n < mol->natoms)
5017 n = -1; /* No such atom */
5018 val = rb_inspect(val);
5020 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
5022 if (n >= 0 && n < mol->natoms)
5024 p = StringValuePtr(val);
5026 rb_raise(rb_eMolbyError, "no such atom: %s", p);
5028 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
5030 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
5031 return 0; /* Not reached */
5035 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
5039 Data_Get_Struct(self, Molecule, mp1);
5040 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
5041 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
5042 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5043 Data_Get_Struct(val, IntGroup, ig);
5044 IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
5053 * Duplicate a molecule. All entries are deep copied, so modifying the newly
5054 * created object does not affect the old object in any sense.
5057 s_Molecule_InitCopy(VALUE self, VALUE arg)
5059 Molecule *mp1, *mp2;
5060 Data_Get_Struct(self, Molecule, mp1);
5061 mp2 = MoleculeFromValue(arg);
5062 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
5063 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
5069 * atom_index(val) -> Integer
5071 * Returns the atom index represented by val. val can be either a non-negative integer
5072 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5073 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
5074 * where resname, resid, name are the residue name, residue id, and atom name respectively.
5075 * If val is a string and multiple atoms match the description, the atom with the lowest index
5079 s_Molecule_AtomIndex(VALUE self, VALUE val)
5082 Data_Get_Struct(self, Molecule, mol);
5083 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5088 * self == Molecule -> boolean
5090 * True if the two arguments point to the same molecule.
5093 s_Molecule_Equal(VALUE self, VALUE val)
5095 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
5096 Molecule *mol1, *mol2;
5097 Data_Get_Struct(self, Molecule, mol1);
5098 Data_Get_Struct(val, Molecule, mol2);
5099 return (mol1 == mol2 ? Qtrue : Qfalse);
5100 } else return Qfalse;
5103 #pragma mark ------ Load/Save ------
5106 s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
5108 if (gLoadSaveErrorMessage != NULL) {
5109 MyAppCallback_setConsoleColor(1);
5110 MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
5111 MyAppCallback_setConsoleColor(0);
5114 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
5119 * loadmbsf(file) -> bool
5121 * Read a structure from a mbsf file.
5122 * Return true if successful.
5125 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
5131 MoleculeClearLoadSaveErrorMessage();
5132 Data_Get_Struct(self, Molecule, mol);
5133 rb_scan_args(argc, argv, "1", &fname);
5134 fstr = FileStringValuePtr(fname);
5135 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5136 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
5142 * loadpsf(file, pdbfile = nil) -> bool
5144 * Read a structure from a psf file. molecule must be empty. The psf may be
5145 * an "extended" version, which also contains coordinates. If pdbfile
5146 * is given, then atomic coordinates are read from that file.
5147 * Return true if successful.
5150 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
5152 VALUE fname, pdbname;
5153 char *fstr, *pdbstr;
5156 Data_Get_Struct(self, Molecule, mol);
5157 if (mol->natoms > 0)
5158 return Qnil; /* Must be a new molecule */
5159 MoleculeClearLoadSaveErrorMessage();
5160 rb_scan_args(argc, argv, "11", &fname, &pdbname);
5161 fstr = FileStringValuePtr(fname);
5162 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5163 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
5165 if (!NIL_P(pdbname)) {
5166 pdbstr = strdup(FileStringValuePtr(pdbname));
5167 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
5169 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
5176 * loadpdb(file) -> bool
5178 * Read coordinates from a pdb file. If molecule is empty, then structure is build
5179 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
5180 * Return true if successful.
5183 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
5189 Data_Get_Struct(self, Molecule, mol);
5190 rb_scan_args(argc, argv, "1", &fname);
5191 MoleculeClearLoadSaveErrorMessage();
5192 fstr = FileStringValuePtr(fname);
5193 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5194 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
5200 * loaddcd(file) -> bool
5202 * Read coordinates from a dcd file. The molecule should not empty.
5203 * Return true if successful.
5206 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
5212 Data_Get_Struct(self, Molecule, mol);
5213 rb_scan_args(argc, argv, "1", &fname);
5214 MoleculeClearLoadSaveErrorMessage();
5215 fstr = FileStringValuePtr(fname);
5216 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5217 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
5223 * loadtep(file) -> bool
5225 * Read coordinates from an ortep .tep file.
5226 * Return true if successful.
5229 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
5235 Data_Get_Struct(self, Molecule, mol);
5236 rb_scan_args(argc, argv, "1", &fname);
5237 MoleculeClearLoadSaveErrorMessage();
5238 fstr = FileStringValuePtr(fname);
5239 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
5240 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
5246 * loadres(file) -> bool
5248 * Read coordinates from a shelx .res file.
5249 * Return true if successful.
5252 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
5258 Data_Get_Struct(self, Molecule, mol);
5259 rb_scan_args(argc, argv, "1", &fname);
5260 MoleculeClearLoadSaveErrorMessage();
5261 fstr = FileStringValuePtr(fname);
5262 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
5263 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
5269 * loadfchk(file) -> bool
5271 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
5272 * Return true if successful.
5275 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
5281 Data_Get_Struct(self, Molecule, mol);
5282 rb_scan_args(argc, argv, "1", &fname);
5283 MoleculeClearLoadSaveErrorMessage();
5284 fstr = FileStringValuePtr(fname);
5285 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
5286 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
5292 * loaddat(file) -> bool
5294 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
5295 * Return true if successful.
5298 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
5304 Data_Get_Struct(self, Molecule, mol);
5305 rb_scan_args(argc, argv, "1", &fname);
5306 MoleculeClearLoadSaveErrorMessage();
5307 fstr = FileStringValuePtr(fname);
5308 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
5309 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
5310 MyAppCallback_hideProgressPanel();
5311 s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
5317 * savembsf(file) -> bool
5319 * Write structure as a mbsf file. Returns true if successful.
5322 s_Molecule_Savembsf(VALUE self, VALUE fname)
5327 Data_Get_Struct(self, Molecule, mol);
5328 MoleculeClearLoadSaveErrorMessage();
5329 fstr = FileStringValuePtr(fname);
5330 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
5331 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
5337 * savepsf(file) -> bool
5339 * Write structure as a psf file. Returns true if successful.
5342 s_Molecule_Savepsf(VALUE self, VALUE fname)
5347 Data_Get_Struct(self, Molecule, mol);
5348 MoleculeClearLoadSaveErrorMessage();
5349 fstr = FileStringValuePtr(fname);
5350 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
5351 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
5357 * savepdb(file) -> bool
5359 * Write coordinates as a pdb file. Returns true if successful.
5362 s_Molecule_Savepdb(VALUE self, VALUE fname)
5367 Data_Get_Struct(self, Molecule, mol);
5368 MoleculeClearLoadSaveErrorMessage();
5369 fstr = FileStringValuePtr(fname);
5370 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
5371 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
5377 * savedcd(file) -> bool
5379 * Write coordinates as a dcd file. Returns true if successful.
5382 s_Molecule_Savedcd(VALUE self, VALUE fname)
5387 Data_Get_Struct(self, Molecule, mol);
5388 MoleculeClearLoadSaveErrorMessage();
5389 fstr = FileStringValuePtr(fname);
5390 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5391 s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
5395 /* load([ftype, ] fname, ...) */
5397 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5400 char *argstr, *methname, *p, *type = "";
5403 const char *ls = (loadFlag ? "load" : "save");
5404 int lslen = strlen(ls);
5409 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5410 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5411 if (argstr[0] == ':') {
5412 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5413 methname = ALLOC_N(char, lslen + strlen(argstr));
5414 strcpy(methname, ls);
5415 strcat(methname, argstr + 1);
5417 for (i = lslen; methname[i] != 0; i++)
5418 methname[i] = tolower(methname[i]);
5419 mid = rb_intern(methname);
5423 rval = rb_funcall2(self, mid, argc, argv);
5429 /* Guess file type from extension */
5430 p = strrchr(argstr, '.');
5434 for (methname = p; *methname != 0; methname++) {
5435 if (!isalpha(*methname))
5438 if (*methname == 0) {
5439 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5440 if (methname == NULL)
5441 rb_raise(rb_eMolbyError, "Low memory");
5442 strcpy(methname, ls);
5443 strcat(methname, p);
5444 for (i = lslen; methname[i] != 0; i++)
5445 methname[i] = tolower(methname[i]);
5446 mid = rb_intern(methname);
5449 if (rb_respond_to(self, mid)) {
5450 /* Load: try to call the load procedure only if it is available */
5451 rval = rb_funcall2(self, mid, argc, argv);
5456 /* Save: call the save procedure, and if not found then call 'method_missing' */
5457 rval = rb_funcall2(self, mid, argc, argv);
5464 rval = rb_str_to_str(argv[0]);
5465 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5466 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5467 return Qnil; /* Does not reach here */
5471 /* Register the path */
5474 Data_Get_Struct(self, Molecule, mol);
5475 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5477 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5478 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5479 if (ap->occupancy != 0.0)
5482 if (i == mol->natoms) {
5483 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5484 ap->occupancy = 1.0;
5493 * molload(file, *args) -> bool
5495 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5496 * file type given by the extension). If this method fails, then all defined (public)
5497 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5500 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5502 return s_Molecule_LoadSave(argc, argv, self, 1);
5507 * molsave(file, *args) -> bool
5509 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5510 * (XXX is the file type given by the extension).
5513 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5515 return s_Molecule_LoadSave(argc, argv, self, 0);
5521 * open(file) -> Molecule
5523 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5526 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5534 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5537 rb_scan_args(argc, argv, "01", &fname);
5541 p = FileStringValuePtr(fname);
5542 iflag = Ruby_SetInterruptFlag(Qfalse);
5543 mp = MoleculeCallback_openNewMolecule(p);
5544 Ruby_SetInterruptFlag(iflag);
5547 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5549 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5551 return ValueFromMolecule(mp);
5557 * new(file, *args) -> Molecule
5559 * Create a new molecule and call "load" method with the same arguments.
5562 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5565 return s_Molecule_Load(argc, argv, self);
5566 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5571 * error_message -> String
5573 * Get the error_message from the last load/save method. If no error, returns nil.
5576 s_Molecule_ErrorMessage(VALUE klass)
5578 if (gLoadSaveErrorMessage == NULL)
5580 else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5585 * set_error_message(String)
5586 * Molecule.error_message = String
5588 * Set the error_message for the present load/save method.
5591 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5593 if (gLoadSaveErrorMessage != NULL) {
5594 free(gLoadSaveErrorMessage);
5595 gLoadSaveErrorMessage = NULL;
5598 sval = rb_str_to_str(sval);
5599 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5606 * set_molecule(Molecule)
5608 * Duplicate the given molecule and set to self. The present molecule must be empty.
5609 * This method is exclusively used for associating a new document with an existing molecule.
5612 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5614 Molecule *mp1, *mp2;
5615 Data_Get_Struct(self, Molecule, mp1);
5616 mp2 = MoleculeFromValue(mval);
5617 MoleculeInitWithMolecule(mp1, mp2);
5618 MoleculeCallback_notifyModification(mp1, 1);
5622 #pragma mark ------ Name attributes ------
5628 * Returns the display name of the molecule. If the molecule has no associated
5629 * document, then returns nil.
5632 s_Molecule_Name(VALUE self)
5636 Data_Get_Struct(self, Molecule, mol);
5637 MoleculeCallback_displayName(mol, buf, sizeof buf);
5641 return Ruby_NewEncodedStringValue2(buf);
5646 * set_name(string) -> self
5648 * Set the name of an untitled molecule. If the molecule is not associated with window
5649 * or it already has an associated file, then exception is thrown.
5652 s_Molecule_SetName(VALUE self, VALUE nval)
5655 Data_Get_Struct(self, Molecule, mol);
5656 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5657 rb_raise(rb_eMolbyError, "Cannot change the window title");
5666 * Returns the full path name of the molecule, if it is associated with a file.
5667 * If the molecule has no associated file, then returns nil.
5670 s_Molecule_Path(VALUE self)
5674 Data_Get_Struct(self, Molecule, mol);
5675 MoleculeCallback_pathName(mol, buf, sizeof buf);
5679 return Ruby_NewFileStringValue(buf);
5686 * Returns the full path name of the directory in which the file associated with the
5687 * molecule is located. If the molecule has no associated file, then returns nil.
5690 s_Molecule_Dir(VALUE self)
5694 Data_Get_Struct(self, Molecule, mol);
5695 MoleculeCallback_pathName(mol, buf, sizeof buf);
5697 translate_char(buf, '\\', '/');
5702 p = strrchr(buf, '/');
5705 return Ruby_NewEncodedStringValue2(buf);
5713 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5714 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5715 * the Molecule structure) is returned.
5718 s_Molecule_Inspect(VALUE self)
5722 Data_Get_Struct(self, Molecule, mol);
5723 MoleculeCallback_displayName(mol, buf, sizeof buf);
5725 /* No associated document */
5726 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5727 return Ruby_NewEncodedStringValue2(buf);
5729 /* Check whether the document name is duplicate */
5733 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5734 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5735 if (strcmp(buf, buf2) == 0) {
5742 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5744 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5746 return Ruby_NewEncodedStringValue2(buf2);
5750 #pragma mark ------ MolEnumerables ------
5753 s_Molecule_MolEnumerable(VALUE self, int kind)
5756 MolEnumerable *mseq;
5757 Data_Get_Struct(self, Molecule, mol);
5758 mseq = MolEnumerableNew(mol, kind);
5759 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5764 * atoms -> MolEnumerable
5766 * Returns a MolEnumerable object representing the array of atoms.
5769 s_Molecule_Atoms(VALUE self)
5771 return s_Molecule_MolEnumerable(self, kAtomKind);
5776 * bonds -> MolEnumerable
5778 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5779 * by an array of two atom indices.
5782 s_Molecule_Bonds(VALUE self)
5784 return s_Molecule_MolEnumerable(self, kBondKind);
5789 * angles -> MolEnumerable
5791 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5792 * by an array of three atom indices.
5795 s_Molecule_Angles(VALUE self)
5797 return s_Molecule_MolEnumerable(self, kAngleKind);
5802 * dihedrals -> MolEnumerable
5804 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5805 * by an array of four atom indices.
5808 s_Molecule_Dihedrals(VALUE self)
5810 return s_Molecule_MolEnumerable(self, kDihedralKind);
5815 * impropers -> MolEnumerable
5817 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5818 * by an array of four atom indices.
5821 s_Molecule_Impropers(VALUE self)
5823 return s_Molecule_MolEnumerable(self, kImproperKind);
5828 * residues -> MolEnumerable
5830 * Returns a MolEnumerable object representing the array of residue names.
5833 s_Molecule_Residues(VALUE self)
5835 return s_Molecule_MolEnumerable(self, kResidueKind);
5842 * Returns the number of atoms.
5845 s_Molecule_Natoms(VALUE self)
5848 Data_Get_Struct(self, Molecule, mol);
5849 return INT2NUM(mol->natoms);
5856 * Returns the number of bonds.
5859 s_Molecule_Nbonds(VALUE self)
5862 Data_Get_Struct(self, Molecule, mol);
5863 return INT2NUM(mol->nbonds);
5868 * nangles -> Integer
5870 * Returns the number of angles.
5873 s_Molecule_Nangles(VALUE self)
5876 Data_Get_Struct(self, Molecule, mol);
5877 return INT2NUM(mol->nangles);
5882 * ndihedrals -> Integer
5884 * Returns the number of dihedrals.
5887 s_Molecule_Ndihedrals(VALUE self)
5890 Data_Get_Struct(self, Molecule, mol);
5891 return INT2NUM(mol->ndihedrals);
5896 * nimpropers -> Integer
5898 * Returns the number of impropers.
5901 s_Molecule_Nimpropers(VALUE self)
5904 Data_Get_Struct(self, Molecule, mol);
5905 return INT2NUM(mol->nimpropers);
5910 * nresidues -> Integer
5912 * Returns the number of residues.
5915 s_Molecule_Nresidues(VALUE self)
5918 Data_Get_Struct(self, Molecule, mol);
5919 return INT2NUM(mol->nresidues);
5924 * nresidues = Integer
5926 * Change the number of residues.
5929 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5932 int ival = NUM2INT(val);
5933 Data_Get_Struct(self, Molecule, mol);
5934 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5935 if (ival != mol->nresidues)
5936 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5942 * max_residue_number(atom_group = nil) -> Integer
5944 * Returns the maximum residue number actually used. If an atom group is given, only
5945 * these atoms are examined. If no atom is present, nil is returned.
5948 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5954 Data_Get_Struct(self, Molecule, mol);
5955 rb_scan_args(argc, argv, "01", &gval);
5956 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5957 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5958 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5963 * min_residue_number(atom_group = nil) -> Integer
5965 * Returns the minimum residue number actually used. If an atom group is given, only
5966 * these atoms are examined. If no atom is present, nil is returned.
5969 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5975 Data_Get_Struct(self, Molecule, mol);
5976 rb_scan_args(argc, argv, "01", &gval);
5977 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5978 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5979 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5984 * each_atom(atom_group = nil) {|aref| ...}
5986 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5987 * group is given, only these atoms are processed.
5988 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5989 * is self (a Molecule object).
5992 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
6000 Data_Get_Struct(self, Molecule, mol);
6001 rb_scan_args(argc, argv, "01", &gval);
6002 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6003 arval = ValueFromMoleculeAndIndex(mol, 0);
6004 Data_Get_Struct(arval, AtomRef, aref);
6005 for (i = 0; i < mol->natoms; i++) {
6007 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6011 IntGroupRelease(ig);
6015 #pragma mark ------ Atom Group ------
6018 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6020 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6021 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6022 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6023 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6030 * atom_group {|aref| ...}
6031 * atom_group(arg1, arg2, ...)
6032 * atom_group(arg1, arg2, ...) {|aref| ...}
6034 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6035 * If arguments are given, then the atoms reprensented by the arguments are added to the
6036 * group. For a conversion of a string to an atom index, see the description
6037 * of Molecule#atom_index.
6038 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6039 * representing each atom, and the atoms are removed from the result if the block returns false.
6043 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6045 IntGroup *ig1, *ig2;
6047 Int i, startPt, interval;
6048 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6049 Data_Get_Struct(retval, IntGroup, ig1);
6050 Data_Get_Struct(self, Molecule, mol);
6052 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6055 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6056 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6057 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6058 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6059 ig2 = IntGroupFromValue(*argv);
6060 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6061 interval = IntGroupGetInterval(ig2, i);
6062 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6064 IntGroupRelease(ig2);
6065 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6067 values[0] = (VALUE)mol;
6068 values[1] = (VALUE)ig1;
6069 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6071 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6076 if (rb_block_given_p()) {
6077 /* Evaluate the given block with an AtomRef as the argument, and delete
6078 the index if the block returns false */
6079 AtomRef *aref = AtomRefNew(mol, 0);
6080 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6081 ig2 = IntGroupNew();
6082 IntGroupCopy(ig2, ig1);
6083 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6085 if (startPt >= mol->natoms)
6087 aref->idx = startPt;
6088 resval = rb_yield(arval);
6090 IntGroupRemove(ig1, startPt, 1);
6092 IntGroupRelease(ig2);
6095 /* Remove points that are out of bounds */
6096 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6103 * selection -> IntGroup
6105 * Returns the current selection.
6108 s_Molecule_Selection(VALUE self)
6113 Data_Get_Struct(self, Molecule, mol);
6114 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6115 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6116 val = ValueFromIntGroup(ig);
6117 IntGroupRelease(ig);
6119 val = IntGroup_Alloc(rb_cIntGroup);
6125 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6129 Data_Get_Struct(self, Molecule, mol);
6133 ig = s_Molecule_AtomGroupFromValue(self, val);
6135 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6137 MoleculeSetSelection(mol, ig);
6139 IntGroupRelease(ig);
6145 * selection = IntGroup
6147 * Set the current selection. The right-hand operand may be nil.
6148 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6151 s_Molecule_SetSelection(VALUE self, VALUE val)
6153 return s_Molecule_SetSelectionSub(self, val, 0);
6158 * set_undoable_selection(IntGroup)
6160 * Set the current selection with undo registration. The right-hand operand may be nil.
6161 * This operation is undoable.
6164 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6166 return s_Molecule_SetSelectionSub(self, val, 1);
6169 #pragma mark ------ Editing ------
6173 * extract(group, dummy_flag = nil) -> Molecule
6175 * Extract the atoms given by group and return as a new molecule object.
6176 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6177 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6178 * names beginning with an underscore) and included in the new molecule object.
6181 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6183 Molecule *mol1, *mol2;
6185 VALUE group, dummy_flag, retval;
6186 Data_Get_Struct(self, Molecule, mol1);
6187 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6188 ig = s_Molecule_AtomGroupFromValue(self, group);
6189 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6192 retval = ValueFromMolecule(mol2);
6194 IntGroupRelease(ig);
6200 * add(molecule2) -> self
6202 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6204 This operation is undoable.
6207 s_Molecule_Add(VALUE self, VALUE val)
6209 Molecule *mol1, *mol2;
6210 Data_Get_Struct(self, Molecule, mol1);
6211 mol2 = MoleculeFromValue(val);
6212 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6218 * remove(group) -> Molecule
6220 * The atoms designated by the given group are removed from the molecule.
6221 * This operation is undoable.
6224 s_Molecule_Remove(VALUE self, VALUE group)
6229 IntGroupIterator iter;
6231 ig = s_Molecule_AtomGroupFromValue(self, group);
6232 /* Data_Get_Struct(self, Molecule, mol1);
6233 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6234 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6235 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6236 Data_Get_Struct(group, IntGroup, ig); */
6237 Data_Get_Struct(self, Molecule, mol1);
6239 /* Remove the bonds between the two fragments */
6240 /* (This is necessary for undo to work correctly) */
6241 IntGroupIteratorInit(ig, &iter);
6243 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6244 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6246 cp = AtomConnectData(&ap->connect);
6247 for (j = 0; j < ap->connect.count; j++) {
6249 if (!IntGroupLookup(ig, n, NULL)) {
6250 /* bond i-n, i is in ig and n is not */
6251 int k = MoleculeLookupBond(mol1, i, n);
6255 IntGroupAdd(bg, k, 1);
6260 IntGroupIteratorRelease(&iter);
6263 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6264 IntGroupRelease(bg);
6267 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6274 * create_atom(name, pos = -1) -> AtomRef
6276 * Create a new atom with the specified name (may contain residue
6277 * information) and position (if position is out of range, the atom is appended at
6278 * the end). Returns the reference to the new atom.
6279 * This operation is undoable.
6282 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6289 char *p, resName[6], atomName[6];
6291 Data_Get_Struct(self, Molecule, mol);
6292 rb_scan_args(argc, argv, "02", &name, &ival);
6294 pos = NUM2INT(rb_Integer(ival));
6297 p = StringValuePtr(name);
6299 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6300 if (atomName[0] == 0)
6301 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6304 if (p == NULL || p[0] == 0) {
6305 memset(atomName, 0, 4);
6308 memset(&arec, 0, sizeof(arec));
6309 strncpy(arec.aname, atomName, 4);
6311 strncpy(arec.resName, resName, 4);
6312 arec.resSeq = resSeq;
6314 arec.occupancy = 1.0;
6315 // i = MoleculeCreateAnAtom(mol, &arec);
6316 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6318 aref = AtomRefNew(mol, pos);
6319 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6324 * duplicate_atom(atomref, pos = -1) -> AtomRef
6326 * Create a new atom with the same attributes (but no bonding information)
6327 * with the specified atom. Returns the reference to the new atom.
6328 * This operation is undoable.
6331 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6337 VALUE retval, aval, ival;
6339 Data_Get_Struct(self, Molecule, mol);
6340 rb_scan_args(argc, argv, "11", &aval, &ival);
6341 if (FIXNUM_P(aval)) {
6342 int idx = NUM2INT(aval);
6343 if (idx < 0 || idx >= mol->natoms)
6344 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6345 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6347 apsrc = s_AtomFromValue(aval);
6350 rb_raise(rb_eMolbyError, "bad atom specification");
6352 pos = NUM2INT(rb_Integer(ival));
6354 AtomDuplicate(&arec, apsrc);
6355 arec.connect.count = 0;
6356 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6359 aref = AtomRefNew(mol, pos);
6360 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6368 * create_bond(n1, n2, ...) -> Integer
6370 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6371 * do nothing for that pair. Returns the number of bonds actually created.
6372 * This operation is undoable.
6375 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6378 Int i, j, k, *ip, old_nbonds;
6380 rb_raise(rb_eMolbyError, "missing arguments");
6382 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6383 Data_Get_Struct(self, Molecule, mol);
6384 ip = ALLOC_N(Int, argc + 1);
6385 for (i = j = 0; i < argc; i++, j++) {
6386 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6388 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6389 j -= 2; /* This bond is already present: skip it */
6391 for (k = 0; k < j - 1; k += 2) {
6392 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6393 j -= 2; /* The same entry is already in the argument */
6400 old_nbonds = mol->nbonds;
6402 ip[j] = kInvalidIndex;
6403 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6407 rb_raise(rb_eMolbyError, "atom index out of range");
6409 rb_raise(rb_eMolbyError, "too many bonds");
6411 rb_raise(rb_eMolbyError, "duplicate bonds");
6413 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6415 rb_raise(rb_eMolbyError, "error in creating bonds");
6416 return INT2NUM(mol->nbonds - old_nbonds);
6421 * molecule.remove_bonds(n1, n2, ...) -> Integer
6423 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6424 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6425 * This operation is undoable.
6428 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6434 rb_raise(rb_eMolbyError, "missing arguments");
6436 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6437 Data_Get_Struct(self, Molecule, mol);
6439 for (i = j = 0; i < argc; i++, j = 1 - j) {
6440 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6442 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6446 IntGroupAdd(bg, k, 1);
6451 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6452 i = IntGroupGetCount(bg);
6453 IntGroupRelease(bg);
6460 * assign_bond_order(idx, d1)
6461 * assign_bond_orders(group, [d1, d2, ...])
6463 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6464 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6465 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6466 * (This may change in the future)
6467 * This operation is undoable.
6470 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6474 Data_Get_Struct(self, Molecule, mol);
6475 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6476 /* The first form */
6477 Int idx = NUM2INT(rb_Integer(idxval));
6478 Double d1 = NUM2DBL(rb_Float(dval));
6479 if (idx < 0 || idx >= mol->nbonds)
6480 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6481 ig = IntGroupNewWithPoints(idx, 1, -1);
6482 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6483 IntGroupRelease(ig);
6487 ig = IntGroupFromValue(idxval);
6488 n = IntGroupGetCount(ig);
6490 rb_raise(rb_eMolbyError, "the bond index is empty");
6491 dval = rb_ary_to_ary(dval);
6492 dp = (Double *)calloc(sizeof(Double), n);
6493 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6494 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6496 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6498 IntGroupRelease(ig);
6505 * get_bond_order(idx) -> Float
6506 * get_bond_orders(group) -> Array
6508 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6509 * In the second form, the bond orders at the indices in the group are returned as an array.
6510 * If no bond order information have been assigned, returns nil (the first form)
6511 * or an empty array (the second form).
6514 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6520 Int i, n, numericArg;
6521 Data_Get_Struct(self, Molecule, mol);
6522 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6523 /* The first form */
6524 Int idx = NUM2INT(rb_Integer(idxval));
6525 if (idx < 0 || idx >= mol->nbonds)
6526 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6527 if (mol->bondOrders == NULL)
6529 ig = IntGroupNewWithPoints(idx, 1, -1);
6533 if (mol->bondOrders == NULL)
6534 return rb_ary_new();
6535 ig = IntGroupFromValue(idxval);
6536 n = IntGroupGetCount(ig);
6538 rb_raise(rb_eMolbyError, "the bond index is empty");
6541 dp = (Double *)calloc(sizeof(Double), n);
6542 MoleculeGetBondOrders(mol, dp, ig);
6544 retval = rb_float_new(dp[0]);
6546 retval = rb_ary_new();
6547 for (i = 0; i < n; i++)
6548 rb_ary_push(retval, rb_float_new(dp[i]));
6551 IntGroupRelease(ig);
6557 * bond_exist?(idx1, idx2) -> bool
6559 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6560 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6563 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6569 Data_Get_Struct(self, Molecule, mol);
6570 idx1 = NUM2INT(rb_Integer(ival1));
6571 idx2 = NUM2INT(rb_Integer(ival2));
6572 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6573 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6574 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6575 cp = AtomConnectData(&ap->connect);
6576 for (i = 0; i < ap->connect.count; i++) {
6585 * add_angle(n1, n2, n3) -> Molecule
6587 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6588 * when a bond is created, so it is rarely necessary to use this method explicitly.
6589 * This operation is undoable.
6592 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6596 Data_Get_Struct(self, Molecule, mol);
6597 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6598 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6599 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6600 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6601 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6602 n[3] = kInvalidIndex;
6603 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6609 * remove_angle(n1, n2, n3) -> Molecule
6611 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6612 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6613 * This operation is undoable.
6616 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6621 Data_Get_Struct(self, Molecule, mol);
6622 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6623 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6624 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6625 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6626 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6627 ig = IntGroupNewWithPoints(n[3], 1, -1);
6628 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6629 IntGroupRelease(ig);
6635 * add_dihedral(n1, n2, n3, n4) -> Molecule
6637 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6638 * when a bond is created, so it is rarely necessary to use this method explicitly.
6639 * This operation is undoable.
6642 s_Molecule_AddDihedral(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 (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6652 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6653 n[4] = kInvalidIndex;
6654 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6660 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6662 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6663 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6664 * This operation is undoable.
6667 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6672 Data_Get_Struct(self, Molecule, mol);
6673 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6674 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6675 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6676 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6677 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6678 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6679 ig = IntGroupNewWithPoints(n[4], 1, -1);
6680 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6681 IntGroupRelease(ig);
6687 * add_improper(n1, n2, n3, n4) -> Molecule
6689 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6690 * not automatically added when a new bond is created, so this method is more useful than
6691 * the angle/dihedral counterpart.
6692 * This operation is undoable.
6695 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6699 Data_Get_Struct(self, Molecule, mol);
6700 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6701 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6702 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6703 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6704 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6705 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6706 n[4] = kInvalidIndex;
6707 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6713 * remove_improper(n1, n2, n3, n4) -> Molecule
6714 * remove_improper(intgroup) -> Molecule
6716 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6717 * Returns self. Unlike angles and dihedrals, impropers are
6718 * not automatically added when a new bond is created, so this method is more useful than
6719 * the angle/dihedral counterpart.
6720 * This operation is undoable.
6723 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6726 VALUE v1, v2, v3, v4;
6729 Data_Get_Struct(self, Molecule, mol);
6731 ig = IntGroupFromValue(argv[0]);
6733 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6734 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6735 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6736 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6737 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6738 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6739 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6740 ig = IntGroupNewWithPoints(n[4], 1, -1);
6742 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6743 IntGroupRelease(ig);
6749 * assign_residue(group, res) -> Molecule
6751 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6752 * or "resname.resno". When the residue number is not specified, the residue number of
6753 * the first atom in the group is used.
6754 * This operation is undoable.
6757 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6761 char *p, *pp, buf[16];
6764 Data_Get_Struct(self, Molecule, mol);
6766 /* Parse the argument res */
6767 if (FIXNUM_P(res)) {
6768 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6769 resid = NUM2INT(res);
6772 p = StringValuePtr(res);
6773 pp = strchr(p, '.');
6775 resid = atoi(pp + 1);
6781 if (n > sizeof buf - 1)
6786 ig = s_Molecule_AtomGroupFromValue(self, range);
6787 if (ig == NULL || IntGroupGetCount(ig) == 0)
6791 /* Use the residue number of the first specified atom */
6792 n = IntGroupGetNthPoint(ig, 0);
6793 if (n >= mol->natoms)
6794 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6795 ap = ATOM_AT_INDEX(mol->atoms, n);
6798 /* Change the residue number */
6799 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6800 /* Change the residue name if necessary */
6804 seqs[1] = kInvalidIndex; */
6805 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6807 IntGroupRelease(ig);
6813 * offset_residue(group, offset) -> Molecule
6815 * Offset the residue number of the specified atoms. If any of the residue number gets
6816 * negative, then exception is thrown.
6817 * This operation is undoable.
6820 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6825 Data_Get_Struct(self, Molecule, mol);
6826 ig = s_Molecule_AtomGroupFromValue(self, range);
6827 ofs = NUM2INT(offset);
6828 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6830 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6831 IntGroupRelease(ig);
6837 * renumber_atoms(array) -> IntGroup
6839 * Change the order of atoms so that the atoms specified in the array argument appear
6840 * in this order from the top of the molecule. The atoms that are not included in array
6841 * are placed after these atoms, and these atoms are returned as an intGroup.
6842 * This operation is undoable.
6845 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6851 VALUE *valp, retval;
6852 Data_Get_Struct(self, Molecule, mol);
6853 if (TYPE(array) != T_ARRAY)
6854 array = rb_funcall(array, rb_intern("to_a"), 0);
6855 n = RARRAY_LEN(array);
6856 valp = RARRAY_PTR(array);
6857 new2old = ALLOC_N(Int, n + 1);
6858 for (i = 0; i < n; i++)
6859 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6860 new2old[i] = kInvalidIndex;
6861 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6863 rb_raise(rb_eMolbyError, "Atom index out of range");
6865 rb_raise(rb_eMolbyError, "Duplicate entry");
6867 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6868 retval = IntGroup_Alloc(rb_cIntGroup);
6869 Data_Get_Struct(retval, IntGroup, ig);
6870 if (mol->natoms > n)
6871 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6878 * set_atom_attr(index, key, value)
6880 * Set the atom attribute for the specified atom.
6881 * This operation is undoable.
6884 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6888 Data_Get_Struct(self, Molecule, mol);
6889 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6890 oldval = s_AtomRef_GetAttr(aref, key);
6893 s_AtomRef_SetAttr(aref, key, val);
6899 * get_atom_attr(index, key)
6901 * Get the atom attribute for the specified atom.
6904 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6906 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6909 #pragma mark ------ Undo Support ------
6913 * register_undo(script, *args)
6915 * Register an undo operation with the current molecule.
6918 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6923 Data_Get_Struct(self, Molecule, mol);
6924 rb_scan_args(argc, argv, "1*", &script, &args);
6925 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6926 MolActionCallback_registerUndo(mol, act);
6932 * undo_enabled? -> bool
6934 * Returns true if undo is enabled for this molecule; otherwise no.
6937 s_Molecule_UndoEnabled(VALUE self)
6940 Data_Get_Struct(self, Molecule, mol);
6941 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6948 * undo_enabled = bool
6950 * Enable or disable undo.
6953 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6956 Data_Get_Struct(self, Molecule, mol);
6957 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6961 #pragma mark ------ Measure ------
6964 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6966 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6967 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6968 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6970 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6976 * center_of_mass(group = nil) -> Vector3D
6978 * Calculate the center of mass for the given set of atoms. The argument
6979 * group is null, then all atoms are considered.
6982 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6988 Data_Get_Struct(self, Molecule, mol);
6989 rb_scan_args(argc, argv, "01", &group);
6990 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6991 s_Molecule_DoCenterOfMass(mol, &v, ig);
6993 IntGroupRelease(ig);
6994 return ValueFromVector(&v);
6999 * centralize(group = nil) -> self
7001 * Translate the molecule so that the center of mass of the given group is located
7002 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7005 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7011 Data_Get_Struct(self, Molecule, mol);
7012 rb_scan_args(argc, argv, "01", &group);
7013 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7014 s_Molecule_DoCenterOfMass(mol, &v, ig);
7016 IntGroupRelease(ig);
7020 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7026 * bounds(group = nil) -> [min, max]
7028 * Calculate the boundary. The return value is an array of two Vector3D objects.
7031 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7039 Data_Get_Struct(self, Molecule, mol);
7040 rb_scan_args(argc, argv, "01", &group);
7041 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7042 if (ig != NULL && IntGroupGetCount(ig) == 0)
7043 rb_raise(rb_eMolbyError, "atom group is empty");
7044 vmin.x = vmin.y = vmin.z = 1e30;
7045 vmax.x = vmax.y = vmax.z = -1e30;
7046 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7048 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7064 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7067 /* Get atom position or a vector */
7069 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7071 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7072 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7073 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7075 VectorFromValue(val, vp);
7081 * measure_bond(n1, n2) -> Float
7083 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7084 * or Vector3D values.
7085 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7088 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7092 Data_Get_Struct(self, Molecule, mol);
7093 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7094 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7095 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7100 * measure_angle(n1, n2, n3) -> Float
7102 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7103 * or Vector3D values. The return value is in degree.
7104 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7107 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7112 Data_Get_Struct(self, Molecule, mol);
7113 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7114 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7115 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7116 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7118 return Qnil; /* Cannot define */
7119 else return rb_float_new(d);
7124 * measure_dihedral(n1, n2, n3, n4) -> Float
7126 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7127 * or Vector3D values. The return value is in degree.
7128 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7131 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7134 Vector v1, v2, v3, v4;
7136 Data_Get_Struct(self, Molecule, mol);
7137 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7138 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7139 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7140 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7141 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7143 return Qnil; /* Cannot define */
7144 else return rb_float_new(d);
7149 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7151 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7152 * first and second atom in the pair should belong to group1 and group2, respectively.
7153 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7156 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7159 VALUE limval, gval1, gval2, rval, igval;
7160 IntGroup *ig1, *ig2;
7161 IntGroupIterator iter1, iter2;
7167 MDExclusion *exinfo;
7170 Data_Get_Struct(self, Molecule, mol);
7171 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7172 lim = NUM2DBL(rb_Float(limval));
7174 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7176 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7178 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7180 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7182 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7184 if (!RTEST(igval)) {
7185 /* Use the exclusion table in MDArena */
7186 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7187 VALUE mval = ValueFromMolecule(mol);
7188 s_RebuildMDParameterIfNecessary(mval, Qnil);
7190 exinfo = mol->arena->exinfo; /* May be NULL */
7191 exlist = mol->arena->exlist;
7196 IntGroupIteratorInit(ig1, &iter1);
7197 IntGroupIteratorInit(ig2, &iter2);
7200 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7202 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7204 if (exinfo != NULL) {
7205 exn1 = exinfo[n[0]].index1;
7206 exn2 = exinfo[n[0] + 1].index1;
7207 } else exn1 = exn2 = -1;
7208 IntGroupIteratorReset(&iter2);
7209 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7210 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7212 continue; /* Same atom */
7213 if (exinfo != NULL) {
7214 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7215 for (i = exn1; i < exn2; i++) {
7216 if (exlist[i] == n[1])
7220 continue; /* Should be excluded */
7222 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7223 /* Is this pair already registered? */
7225 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7226 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7230 /* Not registered yet */
7231 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7236 IntGroupIteratorRelease(&iter2);
7237 IntGroupIteratorRelease(&iter1);
7238 IntGroupRelease(ig2);
7239 IntGroupRelease(ig1);
7240 rval = rb_ary_new2(npairs);
7241 if (pairs != NULL) {
7242 for (i = 0; i < npairs; i++) {
7243 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7252 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7254 * Find atoms that are within the threshold distance from the given atom.
7255 * (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.)
7256 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7257 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7258 * If limit is not given, a default value of 1.2 is used.
7259 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7262 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7265 VALUE aval, limval, radval;
7266 double limit, radius;
7267 Int n1, nbonds, *bonds, an;
7269 Data_Get_Struct(self, Molecule, mol);
7270 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7271 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)) {
7272 VectorFromValue(aval, &v);
7274 radius = gElementParameters[6].radius;
7276 radius = NUM2DBL(rb_Float(radval));
7279 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7280 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7281 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7282 if (an >= 0 && an < gCountElementParameters)
7283 radius = gElementParameters[an].radius;
7284 else radius = gElementParameters[6].radius;
7289 limit = NUM2DBL(rb_Float(limval));
7290 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7292 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7293 aval = rb_ary_new();
7295 for (n1 = 0; n1 < nbonds; n1++)
7296 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7304 * guess_bonds(limit = 1.2) -> Integer
7306 * Create bonds between atoms that are within the threshold distance.
7307 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7308 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7309 * If limit is not given, a default value of 1.2 is used.
7310 * The number of the newly created bonds is returned.
7311 * This operation is undoable.
7314 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7320 Data_Get_Struct(self, Molecule, mol);
7321 rb_scan_args(argc, argv, "01", &limval);
7325 limit = NUM2DBL(rb_Float(limval));
7326 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7328 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7331 return INT2NUM(nbonds);
7334 #pragma mark ------ Cell and Symmetry ------
7338 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7340 * Returns the unit cell parameters. If cell is not set, returns nil.
7343 s_Molecule_Cell(VALUE self)
7348 Data_Get_Struct(self, Molecule, mol);
7349 if (mol->cell == NULL)
7351 val = rb_ary_new2(6);
7352 for (i = 0; i < 6; i++)
7353 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7354 if (mol->cell->has_sigma) {
7355 for (i = 0; i < 6; i++) {
7356 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7364 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7365 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7367 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7368 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7369 This operation is undoable.
7370 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7373 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7377 int i, convert_coord, n;
7379 Data_Get_Struct(self, Molecule, mol);
7380 rb_scan_args(argc, argv, "11", &val, &cval);
7385 val = rb_ary_to_ary(val);
7386 len = RARRAY_LEN(val);
7389 } else if (len >= 6) {
7391 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7392 for (i = 0; i < n; i++)
7393 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7395 convert_coord = (RTEST(cval) ? 1 : 0);
7396 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7402 * box -> [avec, bvec, cvec, origin, flags]
7404 * Get the unit cell information in the form of a periodic bounding box.
7405 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7406 * Integers which define whether the system is periodic along the axis.
7407 * If no unit cell is defined, nil is returned.
7410 s_Molecule_Box(VALUE self)
7414 Data_Get_Struct(self, Molecule, mol);
7415 if (mol == NULL || mol->cell == NULL)
7417 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7418 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7419 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7420 v[3] = ValueFromVector(&(mol->cell->origin));
7421 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7422 val = rb_ary_new4(5, v);
7428 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7429 * set_box(d, origin = [0, 0, 0])
7432 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7433 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7435 Flags, if present, is a 3-member array of Integers defining whether the system is
7436 periodic along the axis.
7437 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7438 In the second form, an isotropic box with cell-length d is set.
7439 In the third form, the existing box is cleared.
7440 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7443 s_Molecule_SetBox(VALUE self, VALUE aval)
7447 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7449 Vector origin = {0, 0, 0};
7452 int i, convertCoordinates = 0;
7453 Data_Get_Struct(self, Molecule, mol);
7455 MolActionCreateAndPerform(mol, gMolActionClearBox);
7458 aval = rb_ary_to_ary(aval);
7459 for (i = 0; i < 6; i++) {
7460 if (i < RARRAY_LEN(aval))
7461 v[i] = (RARRAY_PTR(aval))[i];
7465 MolActionCreateAndPerform(mol, gMolActionClearBox);
7468 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7469 d = NUM2DBL(rb_Float(v[0]));
7470 for (i = 0; i < 3; i++)
7471 VecScale(vv[i], ax[i], d);
7473 VectorFromValue(v[1], &origin);
7474 flags[0] = flags[1] = flags[2] = 1;
7476 for (i = 0; i < 3; i++) {
7479 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7480 d = NUM2DBL(rb_Float(v[i]));
7481 VecScale(vv[i], ax[i], d);
7483 VectorFromValue(v[i], &vv[i]);
7485 flags[i] = (VecLength2(vv[i]) > 0.0);
7488 VectorFromValue(v[3], &origin);
7490 for (i = 0; i < 3; i++) {
7491 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7492 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7496 convertCoordinates = 1;
7498 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7504 * cell_periodicity -> [n1, n2, n3]
7506 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7510 s_Molecule_CellPeriodicity(VALUE self)
7513 Data_Get_Struct(self, Molecule, mol);
7514 if (mol->cell == NULL)
7516 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7521 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7522 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7524 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7525 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7526 * If cell is not defined, exception is raised.
7527 * This operation is undoable.
7530 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7534 Data_Get_Struct(self, Molecule, mol);
7535 if (mol->cell == NULL)
7536 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7539 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7540 flag = NUM2INT(rb_Integer(arg));
7544 arg = rb_ary_to_ary(arg);
7546 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7547 arg0 = RARRAY_PTR(arg)[i];
7548 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7549 flag |= (1 << (2 - i));
7552 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7558 * cell_flexibility -> bool
7560 * Returns the unit cell is flexible or not
7563 s_Molecule_CellFlexibility(VALUE self)
7565 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7568 Data_Get_Struct(self, Molecule, mol);
7569 if (mol->cell == NULL)
7571 if (mol->useFlexibleCell)
7573 else return Qfalse; */
7578 * self.cell_flexibility = bool
7579 * set_cell_flexibility(bool)
7581 * Change the unit cell is flexible or not
7584 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7586 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7589 Data_Get_Struct(self, Molecule, mol);
7590 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7596 * cell_transform -> Transform
7598 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7599 * If cell is not defined, nil is returned.
7602 s_Molecule_CellTransform(VALUE self)
7605 Data_Get_Struct(self, Molecule, mol);
7606 if (mol == NULL || mol->cell == NULL)
7608 return ValueFromTransform(&(mol->cell->tr));
7613 * symmetry -> Array of Transforms
7614 * symmetries -> Array of Transforms
7616 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7617 * returns an empty array.
7620 s_Molecule_Symmetry(VALUE self)
7625 Data_Get_Struct(self, Molecule, mol);
7626 if (mol->nsyms <= 0)
7627 return rb_ary_new();
7628 val = rb_ary_new2(mol->nsyms);
7629 for (i = 0; i < mol->nsyms; i++) {
7630 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7637 * nsymmetries -> Integer
7639 * Get the number of currently defined symmetry operations.
7642 s_Molecule_Nsymmetries(VALUE self)
7645 Data_Get_Struct(self, Molecule, mol);
7646 return INT2NUM(mol->nsyms);
7651 * add_symmetry(Transform) -> Integer
7653 * Add a new symmetry operation. If no symmetry operation is defined and the
7654 * given argument is not an identity transform, then also add an identity
7655 * transform at the index 0.
7656 * Returns the total number of symmetries after operation.
7659 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7663 Data_Get_Struct(self, Molecule, mol);
7664 TransformFromValue(trans, &tr);
7665 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7666 return INT2NUM(mol->nsyms);
7671 * remove_symmetry(count = nil) -> Integer
7672 * remove_symmetries(count = nil) -> Integer
7674 * Remove the specified number of symmetry operations. The last added ones are removed
7675 * first. If count is nil, then all symmetry operations are removed. Returns the
7676 * number of leftover symmetries.
7679 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7684 Data_Get_Struct(self, Molecule, mol);
7685 rb_scan_args(argc, argv, "01", &cval);
7689 n = NUM2INT(rb_Integer(cval));
7690 if (n < 0 || n > mol->nsyms)
7691 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7692 if (n == mol->nsyms)
7695 for (i = 0; i < n; i++)
7696 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7697 return INT2NUM(mol->nsyms);
7702 * wrap_unit_cell(group) -> Vector3D
7704 * Move the specified group so that the center of mass of the group is within the
7705 * unit cell. The offset vector is returned. If no periodic box is defined,
7706 * exception is raised.
7709 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7714 Data_Get_Struct(self, Molecule, mol);
7715 if (mol->cell == NULL)
7716 rb_raise(rb_eMolbyError, "no unit cell is defined");
7717 ig = s_Molecule_AtomGroupFromValue(self, gval);
7718 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7719 TransformVec(&v, mol->cell->rtr, &cv);
7720 if (mol->cell->flags[0])
7722 if (mol->cell->flags[1])
7724 if (mol->cell->flags[2])
7726 TransformVec(&dv, mol->cell->tr, &v);
7728 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7729 IntGroupRelease(ig);
7730 return ValueFromVector(&dv);
7735 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7737 * Expand the specified part of the molecule by the given symmetry operation.
7738 * Returns the array of atom indices corresponding to the expanded atoms.
7739 * If allow_overlap is true, then new atoms are created even when the
7740 * coordinates coincide with the some other atom (special position) of the
7741 * same element; otherwise, such atom will not be created and the index of the
7742 * existing atom is given in the returned array.
7745 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7748 VALUE gval, sval, xval, yval, zval, rval, oval;
7750 Int n[4], allow_overlap;
7754 Data_Get_Struct(self, Molecule, mol);
7755 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7756 n[0] = NUM2INT(rb_Integer(sval));
7757 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7758 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7759 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7760 allow_overlap = (RTEST(oval) ? 1 : 0);
7761 ig = s_Molecule_AtomGroupFromValue(self, gval);
7762 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7763 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7764 natoms = mol->natoms;
7766 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7768 rval = rb_ary_new2(nidx);
7769 while (--nidx >= 0) {
7770 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7772 /* if (natoms == mol->natoms)
7775 rval = IntGroup_Alloc(rb_cIntGroup);
7776 Data_Get_Struct(rval, IntGroup, ig);
7777 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7784 * amend_by_symmetry(group = nil) -> IntGroup
7786 * Expand the specified part of the molecule by the given symmetry operation.
7787 * Returns an IntGroup containing the added atoms.
7790 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7795 Data_Get_Struct(self, Molecule, mol);
7796 rb_scan_args(argc, argv, "01", &gval);
7798 ig = s_Molecule_AtomGroupFromValue(self, gval);
7800 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7801 rval = ValueFromIntGroup(ig2);
7802 IntGroupRelease(ig2);
7806 #pragma mark ------ Transforms ------
7810 * translate(vec, group = nil) -> Molecule
7812 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7813 * This operation is undoable.
7816 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7822 Data_Get_Struct(self, Molecule, mol);
7823 rb_scan_args(argc, argv, "11", &vec, &group);
7824 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7825 VectorFromValue(vec, &v);
7826 // MoleculeTranslate(mol, &v, ig);
7827 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7829 IntGroupRelease(ig);
7835 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7837 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7838 * If group is given, only atoms in the group are moved.
7839 * This operation is undoable.
7842 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7845 volatile VALUE aval, anval, cval, gval;
7850 Data_Get_Struct(self, Molecule, mol);
7851 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7852 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7853 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7854 VectorFromValue(aval, &av);
7856 cv.x = cv.y = cv.z = 0.0;
7858 VectorFromValue(cval, &cv);
7859 if (TransformForRotation(tr, &av, angle, &cv))
7860 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7861 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7863 IntGroupRelease(ig);
7869 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7871 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7872 * axis must not be a zero vector.
7873 * If group is given, only atoms in the group are moved.
7874 * This operation is undoable.
7877 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7880 volatile VALUE aval, cval, gval;
7884 Data_Get_Struct(self, Molecule, mol);
7885 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7886 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7887 VectorFromValue(aval, &av);
7889 cv.x = cv.y = cv.z = 0.0;
7891 VectorFromValue(cval, &cv);
7892 if (TransformForReflection(tr, &av, &cv))
7893 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7894 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7896 IntGroupRelease(ig);
7902 * invert(center = [0,0,0], group = nil) -> Molecule
7904 * Invert the molecule with the given center.
7905 * If group is given, only atoms in the group are moved.
7906 * This operation is undoable.
7909 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7912 volatile VALUE cval, gval;
7916 Data_Get_Struct(self, Molecule, mol);
7917 rb_scan_args(argc, argv, "02", &cval, &gval);
7918 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7920 cv.x = cv.y = cv.z = 0.0;
7922 VectorFromValue(cval, &cv);
7923 TransformForInversion(tr, &cv);
7924 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7926 IntGroupRelease(ig);
7932 * transform(transform, group = nil) -> Molecule
7934 * Transform the molecule by the given Transform object.
7935 * If group is given, only atoms in the group are moved.
7936 * This operation is undoable.
7939 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7945 Data_Get_Struct(self, Molecule, mol);
7946 rb_scan_args(argc, argv, "11", &trans, &group);
7947 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7948 TransformFromValue(trans, &tr);
7949 /* MoleculeTransform(mol, tr, ig); */
7950 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7952 IntGroupRelease(ig);
7958 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7960 * Get the transform corresponding to the symmetry operation. The symop can either be
7961 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7962 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7963 * Otherwise, the returned transform is for fractional coordinates.
7964 * Raises exception when no cell or no transform are defined.
7967 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7973 Data_Get_Struct(self, Molecule, mol);
7974 if (mol->cell == NULL)
7975 rb_raise(rb_eMolbyError, "no unit cell is defined");
7976 if (mol->nsyms == 0)
7977 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7978 rb_scan_args(argc, argv, "11", &sval, &fval);
7979 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7980 symop.sym = NUM2INT(rb_Integer(sval));
7981 symop.dx = symop.dy = symop.dz = 0;
7983 sval = rb_ary_to_ary(sval);
7984 if (RARRAY_LEN(sval) < 4)
7985 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7986 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7987 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7988 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7989 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7991 if (symop.sym >= mol->nsyms)
7992 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7993 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7994 return ValueFromTransform(&tr);
7999 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8001 * Get the symmetry operation corresponding to the given transform.
8002 * If is_cartesian is true, the given transform is for cartesian coordinates.
8003 * Otherwise, the given transform is for fractional coordinates.
8004 * Raises exception when no cell or no transform are defined.
8007 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8014 Data_Get_Struct(self, Molecule, mol);
8015 if (mol->cell == NULL)
8016 rb_raise(rb_eMolbyError, "no unit cell is defined");
8017 if (mol->nsyms == 0)
8018 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8019 rb_scan_args(argc, argv, "11", &tval, &fval);
8020 TransformFromValue(tval, &tr);
8021 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8023 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8025 return Qnil; /* Not found */
8029 #pragma mark ------ Frames ------
8033 * select_frame(index)
8036 * Select the specified frame. If successful, returns true, otherwise returns false.
8039 s_Molecule_SelectFrame(VALUE self, VALUE val)
8042 int ival = NUM2INT(val);
8043 Data_Get_Struct(self, Molecule, mol);
8044 ival = MoleculeSelectFrame(mol, ival, 1);
8054 * Get the current frame.
8057 s_Molecule_Frame(VALUE self)
8060 Data_Get_Struct(self, Molecule, mol);
8061 return INT2NUM(mol->cframe);
8066 * nframes -> Integer
8068 * Get the number of frames.
8071 s_Molecule_Nframes(VALUE self)
8074 Data_Get_Struct(self, Molecule, mol);
8075 return INT2NUM(MoleculeGetNumberOfFrames(mol));
8080 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8081 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8083 * Insert new frames at the indices specified by the intGroup. If the first argument is
8084 * an integer, a single new frame is inserted at that index. If the first argument is
8085 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8086 * should be an array of arrays of Vector3Ds, then those coordinates are set
8087 * to the new frame. Otherwise, the coordinates of current molecule are copied
8089 * Returns an intGroup representing the inserted frames if successful, nil if not.
8092 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8094 VALUE val, coords, cells;
8097 int count, ival, i, j, len, len_c, len2, nframes;
8100 Data_Get_Struct(self, Molecule, mol);
8101 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8102 if (coords != Qnil) {
8103 if (TYPE(coords) != T_ARRAY)
8104 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8105 len = RARRAY_LEN(coords);
8107 if (cells != Qnil) {
8108 if (mol->cell == NULL)
8109 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8110 if (TYPE(cells) != T_ARRAY)
8111 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8112 len_c = RARRAY_LEN(cells);
8114 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
8115 nframes = MoleculeGetNumberOfFrames(mol);
8117 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8118 val = ValueFromIntGroup(ig);
8120 ig = IntGroupFromValue(val);
8122 count = IntGroupGetCount(ig); /* Count is updated here */
8123 vp = ALLOC_N(Vector, mol->natoms * count);
8125 vp2 = ALLOC_N(Vector, 4 * count);
8129 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8130 ptr = RARRAY_PTR(coords);
8131 for (i = 0; i < count; i++) {
8132 if (TYPE(ptr[i]) != T_ARRAY)
8133 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8134 len2 = RARRAY_LEN(ptr[i]);
8135 if (len2 < mol->natoms)
8136 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8137 ptr2 = RARRAY_PTR(ptr[i]);
8138 for (j = 0; j < mol->natoms; j++)
8139 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8143 for (i = 0; i < count; i++) {
8144 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8145 vp[i * mol->natoms + j] = ap->r;
8151 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8152 ptr = RARRAY_PTR(cells);
8153 for (i = 0; i < count; i++) {
8154 if (TYPE(ptr[i]) != T_ARRAY)
8155 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8156 len2 = RARRAY_LEN(ptr[i]);
8158 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8159 ptr2 = RARRAY_PTR(ptr[i]);
8160 for (j = 0; j < 4; j++)
8161 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8164 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8165 IntGroupRelease(ig);
8169 return (ival >= 0 ? val : Qnil);
8174 * create_frame(coordinates = nil) -> Integer
8175 * create_frames(coordinates = nil) -> Integer
8177 * Same as molecule.insert_frames(nil, coordinates).
8180 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8183 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8185 return s_Molecule_InsertFrames(3, vals, self);
8190 * remove_frames(IntGroup, wantCoordinates = false)
8192 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8193 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8194 * removed frames is returned if operation is successful.
8197 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8204 Data_Get_Struct(self, Molecule, mol);
8205 rb_scan_args(argc, argv, "11", &val, &flag);
8206 ig = IntGroupFromValue(val);
8207 count = IntGroupGetCount(ig);
8209 /* Create return value before removing frames */
8214 retval = rb_ary_new2(count);
8215 for (i = 0; i < count; i++) {
8216 n = IntGroupGetNthPoint(ig, i);
8217 coords = rb_ary_new2(mol->natoms);
8218 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8219 if (n < ap->nframes && n != mol->cframe)
8222 rb_ary_push(coords, ValueFromVector(&v));
8224 rb_ary_push(retval, coords);
8226 } else retval = Qtrue;
8227 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8234 * each_frame {|n| ...}
8236 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8237 * the frame number. After completion, the original frame number is restored.
8240 s_Molecule_EachFrame(VALUE self)
8242 int i, cframe, nframes;
8244 Data_Get_Struct(self, Molecule, mol);
8245 cframe = mol->cframe;
8246 nframes = MoleculeGetNumberOfFrames(mol);
8248 for (i = 0; i < nframes; i++) {
8249 MoleculeSelectFrame(mol, i, 1);
8250 rb_yield(INT2NUM(i));
8252 MoleculeSelectFrame(mol, cframe, 1);
8259 * get_coord_from_frame(index, group = nil)
8261 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8262 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8263 * copied; now they are always copied)
8266 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8269 VALUE ival, gval, cval;
8270 Int index, i, j, n, nn;
8272 IntGroupIterator iter;
8275 Data_Get_Struct(self, Molecule, mol);
8276 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8278 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8279 index = NUM2INT(rb_Integer(ival));
8280 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8282 rb_raise(rb_eMolbyError, "No frame is present");
8284 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8287 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8289 ig = s_Molecule_AtomGroupFromValue(self, gval);
8291 n = IntGroupGetCount(ig);
8293 vp = (Vector *)calloc(sizeof(Vector), n);
8294 IntGroupIteratorInit(ig, &iter);
8297 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8298 ap = ATOM_AT_INDEX(mol->atoms, i);
8299 if (index < ap->nframes) {
8300 vp[j] = ap->frames[index];
8308 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8310 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8311 vp = mol->frame_cells + index * 4;
8312 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8314 IntGroupIteratorRelease(&iter);
8316 /* Copy the extra properties */
8317 IntGroupRelease(ig);
8318 for (i = 0; i < mol->nmolprops; i++) {
8319 Double *dp = (Double *)malloc(sizeof(Double));
8321 IntGroupAdd(ig, mol->cframe, 1);
8322 *dp = mol->molprops[i].propvals[index];
8323 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8325 IntGroupRelease(ig);
8333 * reorder_frames(old_indices)
8335 * Reorder the frames. The argument is an array of integers that specify the 'old'
8336 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8337 * same as the old frames 2/0/1, respectively.
8338 * The argument must have the same number of integers as the number of frames.
8341 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8344 Int *ip, *ip2, i, n, nframes;
8345 Data_Get_Struct(self, Molecule, mol);
8346 aval = rb_ary_to_ary(aval);
8347 nframes = MoleculeGetNumberOfFrames(mol);
8348 if (RARRAY_LEN(aval) != nframes)
8349 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8350 ip2 = (Int *)calloc(sizeof(Int), nframes);
8351 ip = (Int *)calloc(sizeof(Int), nframes);
8352 for (i = 0; i < nframes; i++) {
8353 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8354 if (n < 0 || n >= nframes || ip2[n] != 0) {
8357 if (n < 0 || n >= nframes)
8358 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8360 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8366 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8371 #pragma mark ------ Fragments ------
8375 * fragment(n1, *exatoms) -> IntGroup
8376 * fragment(group, *exatoms) -> IntGroup
8378 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8379 * those atoms will not be counted during the search.
8382 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8385 IntGroup *baseg, *ig, *exatoms;
8387 volatile VALUE nval, exval;
8388 Data_Get_Struct(self, Molecule, mol);
8389 rb_scan_args(argc, argv, "1*", &nval, &exval);
8390 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8392 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8394 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8396 if (RARRAY_LEN(exval) == 0) {
8399 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8400 Data_Get_Struct(exval, IntGroup, exatoms);
8402 if (baseg == NULL) {
8403 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8405 IntGroupIterator iter;
8406 IntGroupIteratorInit(baseg, &iter);
8407 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8410 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8412 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8414 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8416 IntGroupAddIntGroup(ig, subg);
8417 IntGroupRelease(subg);
8422 IntGroupIteratorRelease(&iter);
8423 IntGroupRelease(baseg);
8426 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8427 nval = ValueFromIntGroup(ig);
8428 IntGroupRelease(ig);
8434 * fragments(exclude = nil)
8436 * Returns the fragments as an array of IntGroups.
8437 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8438 * in defining the fragment.
8441 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8444 IntGroup *ag, *fg, *eg;
8445 VALUE gval, exval, retval;
8446 Data_Get_Struct(self, Molecule, mol);
8449 if (mol->natoms == 0)
8450 return rb_ary_new();
8451 rb_scan_args(argc, argv, "01", &exval);
8455 eg = IntGroupFromValue(exval);
8456 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8458 IntGroupRemoveIntGroup(ag, eg);
8459 retval = rb_ary_new();
8460 while (IntGroupGetCount(ag) > 0) {
8461 int n = IntGroupGetNthPoint(ag, 0);
8462 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8464 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8465 gval = ValueFromIntGroup(fg);
8466 rb_ary_push(retval, gval);
8467 IntGroupRemoveIntGroup(ag, fg);
8468 IntGroupRelease(fg);
8470 IntGroupRelease(ag);
8472 IntGroupRelease(eg);
8478 * each_fragment(exclude = nil) {|group| ...}
8480 * Execute the block, with the IntGroup object for each fragment as the argument.
8481 * Atoms or bonds should not be added or removed during the execution of the block.
8482 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8483 * in defining the fragment.
8486 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8489 IntGroup *ag, *fg, *eg;
8491 Data_Get_Struct(self, Molecule, mol);
8492 if (mol == NULL || mol->natoms == 0)
8494 rb_scan_args(argc, argv, "01", &exval);
8498 eg = IntGroupFromValue(exval);
8499 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8501 IntGroupRemoveIntGroup(ag, eg);
8502 while (IntGroupGetCount(ag) > 0) {
8503 int n = IntGroupGetNthPoint(ag, 0);
8504 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8506 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8507 gval = ValueFromIntGroup(fg);
8509 IntGroupRemoveIntGroup(ag, fg);
8510 IntGroupRelease(fg);
8512 IntGroupRelease(ag);
8514 IntGroupRelease(eg);
8520 * detachable?(group) -> [n1, n2]
8522 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8523 * of the molecule via only one bond. If it is, then the indices of the atoms
8524 * belonging to the bond is returned, the first element being the atom included
8525 * in the fragment. Otherwise, Qnil is returned.
8528 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8534 Data_Get_Struct(self, Molecule, mol);
8535 ig = s_Molecule_AtomGroupFromValue(self, gval);
8536 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8537 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8538 } else retval = Qnil;
8539 IntGroupRelease(ig);
8545 * bonds_on_border(group = selection) -> Array of Array of two Integers
8547 * Returns an array of bonds that connect an atom in the group and an atom out
8548 * of the group. The first atom in the bond always belongs to the group. If no
8549 * such bonds are present, an empty array is returned.
8552 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8557 Data_Get_Struct(self, Molecule, mol);
8558 rb_scan_args(argc, argv, "01", &gval);
8560 ig = MoleculeGetSelection(mol);
8564 ig = s_Molecule_AtomGroupFromValue(self, gval);
8566 retval = rb_ary_new();
8569 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8571 IntGroupIterator iter;
8573 IntGroupIteratorInit(bg, &iter);
8574 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8575 /* The atoms at the border */
8577 n1 = mol->bonds[i * 2];
8578 n2 = mol->bonds[i * 2 + 1];
8579 if (IntGroupLookupPoint(ig, n1) < 0) {
8583 if (IntGroupLookupPoint(ig, n1) < 0)
8584 continue; /* Actually this is an internal error */
8586 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8588 IntGroupIteratorRelease(&iter);
8590 IntGroupRelease(bg);
8591 IntGroupRelease(ig);
8595 /* Calculate the transform that moves the current coordinates to the reference
8596 coordinates with least displacements. */
8598 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8606 Double eigen_val[3];
8607 Vector eigen_vec[3];
8609 IntGroupIterator iter;
8611 natoms = mol->natoms;
8613 IntGroupIteratorInit(ig, &iter);
8615 /* Calculate the weighted center */
8619 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8620 ap1 = ATOM_AT_INDEX(ap, in);
8621 w1 = (weights != NULL ? weights[i] : ap1->weight);
8622 VecScaleInc(org1, ap1->r, w1);
8623 VecScaleInc(org2, ref[i], w1);
8627 VecScaleSelf(org1, w);
8628 VecScaleSelf(org2, w);
8630 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8631 /* Matrix to diagonalize = R * tR */
8632 memset(r, 0, sizeof(Mat33));
8633 memset(q, 0, sizeof(Mat33));
8634 memset(u, 0, sizeof(Mat33));
8636 IntGroupIteratorReset(&iter);
8637 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8639 ap1 = ATOM_AT_INDEX(ap, in);
8640 w1 = (weights != NULL ? weights[i] : ap1->weight);
8642 VecSub(v1, ap1->r, org1);
8643 VecSub(v2, ref[i], org2);
8644 r[0] += w1 * v1.x * v2.x;
8645 r[1] += w1 * v1.y * v2.x;
8646 r[2] += w1 * v1.z * v2.x;
8647 r[3] += w1 * v1.x * v2.y;
8648 r[4] += w1 * v1.y * v2.y;
8649 r[5] += w1 * v1.z * v2.y;
8650 r[6] += w1 * v1.x * v2.z;
8651 r[7] += w1 * v1.y * v2.z;
8652 r[8] += w1 * v1.z * v2.z;
8655 for (i = 0; i < 9; i++)
8657 for (i = 0; i < 3; i++) {
8658 for (j = 0; j < 3; j++) {
8659 for (k = 0; k < 3; k++) {
8660 q[i+j*3] += r[i+k*3] * r[j+k*3];
8665 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8666 IntGroupIteratorRelease(&iter);
8667 return -1.0; /* Cannot determine the eigenvector */
8670 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8671 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8672 MatrixTranspose(r, r);
8673 for (i = 0; i < 3; i++) {
8674 MatrixVec(&s[i], r, &eigen_vec[i]);
8675 w1 = 1.0 / sqrt(eigen_val[i]);
8676 VecScaleSelf(s[i], w1);
8678 for (k = 0; k < 3; k++) {
8679 u[0] += s[k].x * eigen_vec[k].x;
8680 u[1] += s[k].y * eigen_vec[k].x;
8681 u[2] += s[k].z * eigen_vec[k].x;
8682 u[3] += s[k].x * eigen_vec[k].y;
8683 u[4] += s[k].y * eigen_vec[k].y;
8684 u[5] += s[k].z * eigen_vec[k].y;
8685 u[6] += s[k].x * eigen_vec[k].z;
8686 u[7] += s[k].y * eigen_vec[k].z;
8687 u[8] += s[k].z * eigen_vec[k].z;
8690 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8691 MatrixVec(&org1, u, &org1);
8693 for (i = 0; i < 9; i++)
8699 /* Calculate rmsd */
8700 IntGroupIteratorReset(&iter);
8702 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8704 ap1 = ATOM_AT_INDEX(ap, in);
8705 TransformVec(&tv, trans, &ap1->r);
8707 w += VecLength2(tv);
8710 IntGroupIteratorRelease(&iter);
8716 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8718 * Calculate the transform to fit the given group to the set of reference coordinates.
8719 * The reference coordinates ref is given as either a frame number, an array of
8720 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8721 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8722 * Return values are the transform (that converts the present coordinates to the
8723 * target coordinates) and root mean square deviation (without weight).
8726 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8730 VALUE gval, rval, wval;
8732 IntGroupIterator iter;
8733 int nn, errnum, i, j, in, status;
8735 Double *weights, dval[3];
8738 Data_Get_Struct(self, Molecule, mol);
8739 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8741 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8743 ig = s_Molecule_AtomGroupFromValue(self, gval);
8744 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8745 IntGroupRelease(ig);
8746 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8748 ref = (Vector *)calloc(sizeof(Vector), nn);
8749 weights = (Double *)calloc(sizeof(Double), nn);
8750 IntGroupIteratorInit(ig, &iter);
8751 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8752 int fn = NUM2INT(rb_Integer(rval));
8753 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8758 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8759 ap = ATOM_AT_INDEX(mol->atoms, in);
8760 if (fn < ap->nframes)
8761 ref[i] = ap->frames[fn];
8762 else ref[i] = ap->r;
8764 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8765 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8766 if (m->row * m->column < nn * 3) {
8770 for (i = 0; i < nn; i++) {
8771 ref[i].x = m->data[i * 3];
8772 ref[i].y = m->data[i * 3 + 1];
8773 ref[i].z = m->data[i * 3 + 2];
8777 rval = rb_protect(rb_ary_to_ary, rval, &status);
8782 if (RARRAY_LEN(rval) < nn) {
8786 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8787 /* Array of 3*nn numbers */
8788 if (RARRAY_LEN(rval) < nn * 3) {
8792 for (i = 0; i < nn; i++) {
8793 for (j = 0; j < 3; j++) {
8794 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8799 dval[j] = NUM2DBL(aval);
8806 /* Array of nn Vector3Ds or Arrays */
8807 for (i = 0; i < nn; i++) {
8808 aval = (RARRAY_PTR(rval))[i];
8809 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8810 VectorFromValue(aval, &ref[i]);
8812 aval = rb_protect(rb_ary_to_ary, aval, &status);
8817 if (RARRAY_LEN(aval) < 3) {
8822 for (j = 0; j < 3; j++) {
8823 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8828 dval[j] = NUM2DBL(aaval);
8838 /* Use atomic weights */
8839 IntGroupIteratorReset(&iter);
8840 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8841 ap = ATOM_AT_INDEX(mol->atoms, in);
8842 weights[i] = ap->weight;
8845 wval = rb_protect(rb_ary_to_ary, wval, &status);
8850 if (RARRAY_LEN(wval) < nn) {
8854 for (i = 0; i < nn; i++) {
8855 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8860 weights[i] = NUM2DBL(wwval);
8863 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8870 IntGroupIteratorRelease(&iter);
8874 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8875 } else if (errnum == 1) {
8876 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8877 } else if (errnum == 2) {
8878 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8879 } else if (errnum == 3) {
8880 rb_jump_tag(status);
8881 } else if (errnum == 4) {
8882 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8883 } else if (errnum == 5) {
8884 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8885 } else if (errnum == 6) {
8886 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8888 return Qnil; /* Not reached */
8891 #pragma mark ------ Screen Display ------
8897 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8900 s_Molecule_Display(VALUE self)
8903 Data_Get_Struct(self, Molecule, mol);
8904 if (mol->mview != NULL)
8905 MainViewCallback_display(mol->mview);
8913 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8916 s_Molecule_MakeFront(VALUE self)
8919 Data_Get_Struct(self, Molecule, mol);
8920 if (mol->mview != NULL)
8921 MainViewCallback_makeFront(mol->mview);
8927 * update_enabled? -> bool
8929 * Returns true if screen update is enabled; otherwise no.
8932 s_Molecule_UpdateEnabled(VALUE self)
8935 Data_Get_Struct(self, Molecule, mol);
8936 if (mol->mview != NULL && !mol->mview->freezeScreen)
8943 * update_enabled = bool
8945 * Enable or disable screen update. This is effective for automatic update on modification.
8946 * Explicit call to molecule.display() always updates the screen.
8949 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8952 Data_Get_Struct(self, Molecule, mol);
8953 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8954 if (mol->mview != NULL)
8955 mol->mview->freezeScreen = (val == Qfalse);
8963 * show_unitcell(bool)
8964 * show_unitcell = bool
8966 * Set the flag whether to show the unit cell. If no argument is given, the
8967 * current flag is returned.
8970 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8973 Data_Get_Struct(self, Molecule, mol);
8974 if (mol->mview == NULL)
8977 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8978 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8980 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8986 * show_hydrogens(bool)
8987 * show_hydrogens = bool
8989 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8990 * current flag is returned.
8993 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8996 Data_Get_Struct(self, Molecule, mol);
8997 if (mol->mview == NULL)
9000 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9001 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9003 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9009 * show_dummy_atoms(bool)
9010 * show_dummy_atoms = bool
9012 * Set the flag whether to show the dummy atoms. If no argument is given, the
9013 * current flag is returned.
9016 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9019 Data_Get_Struct(self, Molecule, mol);
9020 if (mol->mview == NULL)
9023 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9024 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9026 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9032 * show_expanded(bool)
9033 * show_expanded = bool
9035 * Set the flag whether to show the expanded atoms. If no argument is given, the
9036 * current flag is returned.
9039 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9042 Data_Get_Struct(self, Molecule, mol);
9043 if (mol->mview == NULL)
9046 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9047 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9049 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9055 * show_ellipsoids(bool)
9056 * show_ellipsoids = bool
9058 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9059 * current flag is returned.
9062 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9065 Data_Get_Struct(self, Molecule, mol);
9066 if (mol->mview == NULL)
9069 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9070 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9072 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9077 * is_atom_visible(index) -> Boolean
9079 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9080 * as well as the molecule attributes (showHydrogens, etc.)
9083 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9088 Data_Get_Struct(self, Molecule, mol);
9089 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9090 if (idx < 0 || idx >= mol->natoms)
9092 ap = ATOM_AT_INDEX(mol->atoms, idx);
9093 if (mol->mview != NULL) {
9094 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9096 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9098 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9101 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9106 * hidden_atoms -> IntGroup
9108 * Returns the currently hidden atoms.
9111 s_Molecule_HiddenAtoms(VALUE self)
9113 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9114 return Qnil; /* Not reached */
9119 * set_hidden_atoms(IntGroup)
9120 * self.hidden_atoms = IntGroup
9122 * Hide the specified atoms. This operation is _not_ undoable.
9125 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9127 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9128 return Qnil; /* Not reached */
9133 * show_graphite -> Integer
9134 * show_graphite = Integer
9135 * show_graphite = boolean
9137 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9138 * number of rings to display for each direction.
9139 * If the argument is boolean, only the show/hide flag is set.
9142 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9145 Data_Get_Struct(self, Molecule, mol);
9146 if (mol->mview == NULL)
9149 if (argv[0] == Qnil || argv[0] == Qfalse)
9150 mol->mview->showGraphiteFlag = 0;
9151 else if (argv[0] == Qtrue)
9152 mol->mview->showGraphiteFlag = 1;
9154 int n = NUM2INT(rb_Integer(argv[0]));
9156 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9157 mol->mview->showGraphite = n;
9159 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9161 return INT2NUM(mol->mview->showGraphite);
9166 * show_graphite? -> boolean
9168 * Return whether the graphite is set visible or not.
9171 s_Molecule_ShowGraphiteFlag(VALUE self)
9174 Data_Get_Struct(self, Molecule, mol);
9175 if (mol->mview == NULL)
9177 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9182 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9183 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9184 * show_periodic_image = boolean
9186 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9187 * set but no visual effects are observed.
9188 * If the argument is boolean, only the show/hide flag is modified.
9191 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9197 Data_Get_Struct(self, Molecule, mol);
9198 if (mol->mview == NULL)
9200 rb_scan_args(argc, argv, "01", &val);
9202 /* Change current settings */
9203 if (val == Qnil || val == Qfalse)
9204 mol->mview->showPeriodicImageFlag = 0;
9205 else if (val == Qtrue)
9206 mol->mview->showPeriodicImageFlag = 1;
9208 val = rb_ary_to_ary(val);
9209 for (i = 0; i < 6; i++) {
9210 if (i < RARRAY_LEN(val))
9211 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9213 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9214 rb_raise(rb_eMolbyError, "bad arguments");
9215 for (i = 0; i < 6; i++)
9216 mol->mview->showPeriodicImage[i] = ival[i];
9218 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9221 for (i = 0; i < 6; i++)
9222 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9228 * show_periodic_image? -> boolean
9230 * Return whether the periodic images are set to visible or not. This flag is
9231 * independent from the show_periodic_image settings.
9234 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9237 Data_Get_Struct(self, Molecule, mol);
9238 if (mol->mview == NULL)
9240 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9245 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9246 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9247 * show_rotation_center = boolean
9249 * Set to show the rotation center of the screen.
9250 * If the argument is boolean, only the show/hide flag is modified.
9253 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9256 Data_Get_Struct(self, Molecule, mol);
9257 if (mol->mview == NULL)
9260 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9261 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9263 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9272 * Set the flag whether to draw the model in line mode. If no argument is given, the
9273 * current flag is returned.
9276 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9279 Data_Get_Struct(self, Molecule, mol);
9280 if (mol->mview == NULL)
9283 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9284 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9286 return (mol->mview->lineMode ? Qtrue : Qfalse);
9291 * atom_radius = float
9294 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9296 * If no argument is given, the current value is returned.
9299 s_Molecule_AtomRadius(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->atomRadius = rad;
9309 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9313 return rb_float_new(mol->mview->atomRadius);
9318 * bond_radius = float
9321 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9322 * If no argument is given, the current value is returned.
9325 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9328 Data_Get_Struct(self, Molecule, mol);
9329 if (mol->mview == NULL)
9332 double rad = NUM2DBL(rb_Float(argv[0]));
9334 mol->mview->bondRadius = rad;
9335 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9339 return rb_float_new(mol->mview->bondRadius);
9344 * atom_resolution = integer
9347 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9348 * (Default = 12; minimum = 6)
9349 * If no argument is given, the current value is returned.
9352 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9355 Data_Get_Struct(self, Molecule, mol);
9356 if (mol->mview == NULL)
9359 int res = NUM2INT(rb_Integer(argv[0]));
9361 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9362 mol->mview->atomResolution = res;
9363 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9364 return INT2NUM(res);
9366 return INT2NUM(mol->mview->atomResolution);
9371 * bond_resolution = integer
9374 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9375 * (Default = 8; minimum = 4)
9376 * If no argument is given, the current value is returned.
9379 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9382 Data_Get_Struct(self, Molecule, mol);
9383 if (mol->mview == NULL)
9386 int res = NUM2INT(rb_Integer(argv[0]));
9388 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9389 mol->mview->bondResolution = res;
9390 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9391 return INT2NUM(res);
9393 return INT2NUM(mol->mview->bondResolution);
9400 * Resize the model drawing to fit in the window.
9403 s_Molecule_ResizeToFit(VALUE self)
9406 Data_Get_Struct(self, Molecule, mol);
9407 if (mol->mview != NULL)
9408 MainView_resizeToFit(mol->mview);
9414 * get_view_rotation -> [[ax, ay, az], angle]
9416 * Get the current rotation for the view. Angle is in degree, not radian.
9419 s_Molecule_GetViewRotation(VALUE self)
9424 Data_Get_Struct(self, Molecule, mol);
9425 if (mol->mview == NULL)
9427 TrackballGetRotate(mol->mview->track, f);
9428 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9432 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9437 * get_view_scale -> float
9439 * Get the current scale for the view.
9442 s_Molecule_GetViewScale(VALUE self)
9445 Data_Get_Struct(self, Molecule, mol);
9446 if (mol->mview == NULL)
9448 return rb_float_new(TrackballGetScale(mol->mview->track));
9453 * get_view_center -> Vector
9455 * Get the current center point of the view.
9458 s_Molecule_GetViewCenter(VALUE self)
9463 Data_Get_Struct(self, Molecule, mol);
9464 if (mol->mview == NULL)
9466 TrackballGetTranslate(mol->mview->track, f);
9467 v.x = -f[0] * mol->mview->dimension;
9468 v.y = -f[1] * mol->mview->dimension;
9469 v.z = -f[2] * mol->mview->dimension;
9470 return ValueFromVector(&v);
9475 * set_view_rotation([ax, ay, az], angle) -> self
9477 * Set the current rotation for the view. Angle is in degree, not radian.
9480 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9485 Data_Get_Struct(self, Molecule, mol);
9486 if (mol->mview == NULL)
9488 VectorFromValue(aval, &v);
9489 if (NormalizeVec(&v, &v))
9490 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9494 f[0] = -NUM2DBL(rb_Float(angval));
9495 TrackballSetRotate(mol->mview->track, f);
9496 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9502 * set_view_scale(scale) -> self
9504 * Set the current scale for the view.
9507 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9510 Data_Get_Struct(self, Molecule, mol);
9511 if (mol->mview == NULL)
9513 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9514 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9520 * set_view_center(vec) -> self
9522 * Set the current center point of the view.
9525 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9530 Data_Get_Struct(self, Molecule, mol);
9531 if (mol->mview == NULL)
9533 VectorFromValue(aval, &v);
9534 f[0] = -v.x / mol->mview->dimension;
9535 f[1] = -v.y / mol->mview->dimension;
9536 f[2] = -v.z / mol->mview->dimension;
9537 TrackballSetTranslate(mol->mview->track, f);
9538 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9544 * set_background_color(red, green, blue)
9546 * Set the background color of the model window.
9549 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9552 Data_Get_Struct(self, Molecule, mol);
9553 if (mol->mview != NULL) {
9554 VALUE rval, gval, bval;
9555 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9556 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9563 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9565 * Export the current graphic to a PNG or TIF file (determined by the extension).
9566 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9567 * If either width or height is not specified, then the screen width/height is used instead.
9570 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9573 VALUE fval, sval, bval, wval, hval;
9576 int bg_color, width, height;
9577 Data_Get_Struct(self, Molecule, mol);
9578 if (mol->mview == NULL)
9579 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9580 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9581 fname = FileStringValuePtr(fval);
9584 else scale = NUM2DBL(rb_Float(sval));
9587 else bg_color = NUM2INT(rb_Integer(bval));
9590 else width = NUM2INT(rb_Integer(wval));
9593 else height = NUM2INT(rb_Integer(hval));
9594 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9599 #pragma mark ------ Graphics ------
9602 s_CalculateGraphicNormals(MainViewGraphic *gp)
9606 if (gp == NULL || gp->npoints < 3)
9608 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9609 v1.x = gp->points[3] - gp->points[0];
9610 v1.y = gp->points[4] - gp->points[1];
9611 v1.z = gp->points[5] - gp->points[2];
9612 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9613 for (i = 2; i < gp->npoints; i++) {
9614 v2.x = gp->points[i * 3] - gp->points[0];
9615 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9616 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9617 VecCross(v3, v1, v2);
9618 NormalizeVec(&v3, &v3);
9619 gp->normals[i * 3] = v3.x;
9620 gp->normals[i * 3 + 1] = v3.y;
9621 gp->normals[i * 3 + 2] = v3.z;
9624 /* normals[0] = average of all nv[i] (i=2..n-1) */
9626 for (i = 2; i < gp->npoints; i++) {
9627 v1.x += gp->normals[i * 3];
9628 v1.y += gp->normals[i * 3 + 1];
9629 v1.z += gp->normals[i * 3 + 2];
9631 NormalizeVec(&v1, &v1);
9632 gp->normals[0] = v1.x;
9633 gp->normals[1] = v1.y;
9634 gp->normals[2] = v1.z;
9635 /* normals[1] = nv[2].normalize */
9636 v2.x = gp->normals[6];
9637 v2.y = gp->normals[7];
9638 v2.z = gp->normals[8];
9639 NormalizeVec(&v1, &v2);
9640 gp->normals[3] = v1.x;
9641 gp->normals[4] = v1.y;
9642 gp->normals[5] = v1.z;
9643 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9644 for (i = 2; i < gp->npoints; i++) {
9645 if (i == gp->npoints - 1)
9648 v3.x = gp->normals[i * 3 + 3];
9649 v3.y = gp->normals[i * 3 + 4];
9650 v3.z = gp->normals[i * 3 + 5];
9653 NormalizeVec(&v1, &v2);
9654 gp->normals[i * 3] = v1.x;
9655 gp->normals[i * 3 + 1] = v1.y;
9656 gp->normals[i * 3 + 2] = v1.z;
9663 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9665 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9666 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9667 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9668 * points: an array of Vectors
9672 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9678 VALUE kval, cval, pval, fval, ival;
9679 Data_Get_Struct(self, Molecule, mol);
9680 if (mol->mview == NULL)
9681 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9682 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9683 idx = NUM2INT(rb_Integer(ival));
9685 idx = mol->mview->ngraphics;
9686 else if (idx < 0 || idx > mol->mview->ngraphics)
9687 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9688 memset(&g, 0, sizeof(g));
9690 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9691 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9693 kval = rb_obj_as_string(kval);
9694 p = StringValuePtr(kval);
9695 if (strcmp(p, "line") == 0)
9696 g.kind = kMainViewGraphicLine;
9697 else if (strcmp(p, "poly") == 0)
9698 g.kind = kMainViewGraphicPoly;
9699 else if (strcmp(p, "cylinder") == 0)
9700 g.kind = kMainViewGraphicCylinder;
9701 else if (strcmp(p, "cone") == 0)
9702 g.kind = kMainViewGraphicCone;
9703 else if (strcmp(p, "ellipsoid") == 0)
9704 g.kind = kMainViewGraphicEllipsoid;
9705 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9707 g.closed = (RTEST(fval) ? 1 : 0);
9708 cval = rb_ary_to_ary(cval);
9709 n = RARRAY_LEN(cval);
9710 if (n < 3 || n >= 5)
9711 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9714 for (i = 0; i < n; i++)
9715 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9716 pval = rb_ary_to_ary(pval);
9717 n = RARRAY_LEN(pval);
9718 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9720 rb_raise(rb_eArgError, "no control points are given");
9722 case kMainViewGraphicLine:
9724 rb_raise(rb_eArgError, "the line object must have at least two control points");
9726 case kMainViewGraphicPoly:
9728 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9730 case kMainViewGraphicCylinder:
9731 case kMainViewGraphicCone:
9733 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9736 case kMainViewGraphicEllipsoid:
9740 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9743 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9744 for (i = 0; i < n; i++) {
9746 VALUE rval = RARRAY_PTR(pval)[i];
9748 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9749 /* The float argument can also be given as a vector (for simplify undo registration) */
9750 VectorFromValue(rval, &v);
9752 v.x = NUM2DBL(rb_Float(rval));
9756 VectorFromValue(rval, &v);
9758 g.points[i * 3] = v.x;
9759 g.points[i * 3 + 1] = v.y;
9760 g.points[i * 3 + 2] = v.z;
9762 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9764 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9765 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9766 g.points[7] = g.points[11] = g.points[3];
9768 if (g.kind == kMainViewGraphicPoly) {
9769 /* Calculate normals */
9770 s_CalculateGraphicNormals(&g);
9772 MainView_insertGraphic(mol->mview, idx, &g);
9777 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9778 MolActionCallback_registerUndo(mol, act);
9779 MolActionRelease(act);
9782 return INT2NUM(idx);
9787 * create_graphic(kind, color, points, fill = nil) -> integer
9789 * Create a new graphic object. The arguments are similar as insert_graphic.
9792 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9795 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9796 args[0] = INT2NUM(-1);
9797 return s_Molecule_InsertGraphic(argc + 1, args, self);
9802 * remove_graphic(index) -> integer
9804 * Remove a graphic object.
9807 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9811 Data_Get_Struct(self, Molecule, mol);
9812 if (mol->mview == NULL)
9813 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9814 i = NUM2INT(rb_Integer(ival));
9815 if (i < 0 || i >= mol->mview->ngraphics)
9816 rb_raise(rb_eArgError, "graphic index is out of range");
9818 /* Prepare data for undo */
9819 MainViewGraphic *gp;
9824 gp = mol->mview->graphics + i;
9825 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9826 for (n = 0; n < gp->npoints; n++) {
9827 vp[n].x = gp->points[n * 3];
9828 vp[n].y = gp->points[n * 3 + 1];
9829 vp[n].z = gp->points[n * 3 + 2];
9831 col[0] = gp->rgba[0];
9832 col[1] = gp->rgba[1];
9833 col[2] = gp->rgba[2];
9834 col[3] = gp->rgba[3];
9835 if (gp->visible == 0) {
9836 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9837 MolActionCallback_registerUndo(mol, act);
9838 MolActionRelease(act);
9840 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9841 MolActionCallback_registerUndo(mol, act);
9843 MolActionRelease(act);
9845 MainView_removeGraphic(mol->mview, i);
9851 * ngraphics -> integer
9853 * Get the number of graphic objects.
9856 s_Molecule_NGraphics(VALUE self)
9859 Data_Get_Struct(self, Molecule, mol);
9860 if (mol->mview == NULL)
9861 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9862 return INT2NUM(mol->mview->ngraphics);
9867 * get_graphic_point(graphic_index, point_index) -> value
9868 * get_graphic_points(graphic_index) -> values
9870 * Get the point_index-th control point of graphic_index-th graphic object.
9871 * Get an array of all control points with the given values.
9875 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9877 MainViewGraphic *gp;
9882 Data_Get_Struct(self, Molecule, mol);
9883 if (mol->mview == NULL)
9884 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9885 rb_scan_args(argc, argv, "11", &gval, &pval);
9886 index = NUM2INT(rb_Integer(gval));
9887 if (index < 0 || index >= mol->mview->ngraphics)
9888 rb_raise(rb_eArgError, "the graphic index is out of range");
9889 gp = mol->mview->graphics + index;
9891 pindex = NUM2INT(rb_Integer(pval));
9892 if (pindex < 0 || pindex >= gp->npoints)
9893 rb_raise(rb_eArgError, "the point index is out of range");
9894 v.x = gp->points[pindex * 3];
9895 v.y = gp->points[pindex * 3 + 1];
9896 v.z = gp->points[pindex * 3 + 2];
9897 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9898 return rb_float_new(v.x);
9900 return ValueFromVector(&v);
9903 pval = rb_ary_new();
9904 for (pindex = 0; pindex < gp->npoints; pindex++) {
9905 v.x = gp->points[pindex * 3];
9906 v.y = gp->points[pindex * 3 + 1];
9907 v.z = gp->points[pindex * 3 + 2];
9908 rb_ary_push(pval, ValueFromVector(&v));
9916 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9917 * set_graphic_points(graphic_index, new_values) -> new_values
9919 * Change the point_index-th control point of graphic_index-th graphic object.
9920 * Replace the control points with the given values.
9924 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9926 MainViewGraphic *gp;
9930 VALUE gval, pval, nval;
9932 Data_Get_Struct(self, Molecule, mol);
9933 if (mol->mview == NULL)
9934 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9935 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9936 index = NUM2INT(rb_Integer(gval));
9937 if (index < 0 || index >= mol->mview->ngraphics)
9938 rb_raise(rb_eArgError, "the graphic index is out of range");
9939 gp = mol->mview->graphics + index;
9941 pindex = NUM2INT(rb_Integer(pval));
9942 if (pindex < 0 || pindex >= gp->npoints)
9943 rb_raise(rb_eArgError, "the point index is out of range");
9944 v0.x = gp->points[pindex * 3];
9945 v0.y = gp->points[pindex * 3 + 1];
9946 v0.z = gp->points[pindex * 3 + 2];
9947 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9948 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9949 v.x = NUM2DBL(rb_Float(nval));
9951 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9952 v.x = NUM2DBL(rb_Float(nval));
9954 gp->points[7] = gp->points[11] = v.x;
9955 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9956 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9959 v.x = kInvalidFloat;
9961 } else VectorFromValue(nval, &v);
9963 gp->points[pindex * 3] = v.x;
9964 gp->points[pindex * 3 + 1] = v.y;
9965 gp->points[pindex * 3 + 2] = v.z;
9966 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9970 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9971 for (pindex = 0; pindex < gp->npoints; pindex++) {
9972 vp[pindex].x = gp->points[pindex * 3];
9973 vp[pindex].y = gp->points[pindex * 3 + 1];
9974 vp[pindex].z = gp->points[pindex * 3 + 2];
9976 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9978 pval = rb_ary_to_ary(pval);
9979 len = RARRAY_LEN(pval);
9980 if (gp->npoints < len) {
9981 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9983 } else if (gp->npoints > len) {
9986 case kMainViewGraphicLine: len2 = 2; break;
9987 case kMainViewGraphicPoly: len2 = 3; break;
9988 case kMainViewGraphicCylinder: len2 = 3; break;
9989 case kMainViewGraphicCone: len2 = 3; break;
9990 case kMainViewGraphicEllipsoid: len2 = 4; break;
9996 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9997 aval = RARRAY_PTR(pval)[pindex];
9998 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9999 v.x = NUM2DBL(rb_Float(aval));
10001 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
10002 v.x = NUM2DBL(rb_Float(aval));
10004 gp->points[7] = gp->points[11] = v.x;
10005 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
10007 } else VectorFromValue(aval, &v);
10008 gp->points[pindex * 3] = v.x;
10009 gp->points[pindex * 3 + 1] = v.y;
10010 gp->points[pindex * 3 + 2] = v.z;
10013 if (gp->kind == kMainViewGraphicPoly) {
10014 /* Calculate normals */
10015 s_CalculateGraphicNormals(gp);
10017 MolActionCallback_registerUndo(mol, act);
10018 MolActionRelease(act);
10019 MoleculeCallback_notifyModification(mol, 0);
10025 * get_graphic_color(graphic_index) -> value
10027 * Get the color of graphic_index-th graphic object
10030 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10032 MainViewGraphic *gp;
10035 Data_Get_Struct(self, Molecule, mol);
10036 if (mol->mview == NULL)
10037 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10038 index = NUM2INT(rb_Integer(gval));
10039 if (index < 0 || index >= mol->mview->ngraphics)
10040 rb_raise(rb_eArgError, "the graphic index is out of range");
10041 gp = mol->mview->graphics + index;
10042 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]));
10047 * set_graphic_color(graphic_index, new_value) -> new_value
10049 * Change the color of graphic_index-th graphic object
10053 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10055 MainViewGraphic *gp;
10060 Data_Get_Struct(self, Molecule, mol);
10061 if (mol->mview == NULL)
10062 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10063 index = NUM2INT(rb_Integer(gval));
10064 if (index < 0 || index >= mol->mview->ngraphics)
10065 rb_raise(rb_eArgError, "the graphic index is out of range");
10066 gp = mol->mview->graphics + index;
10067 for (i = 0; i < 4; i++)
10068 c[i] = gp->rgba[i];
10069 cval = rb_ary_to_ary(cval);
10070 n = RARRAY_LEN(cval);
10071 if (n != 3 && n != 4)
10072 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10074 for (i = 0; i < n; i++) {
10075 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10079 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10080 MolActionCallback_registerUndo(mol, act);
10081 MolActionRelease(act);
10082 MoleculeCallback_notifyModification(mol, 0);
10088 * show_graphic(graphic_index) -> self
10090 * Enable the visible flag of the graphic_index-th graphic object
10094 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10096 MainViewGraphic *gp;
10099 Data_Get_Struct(self, Molecule, mol);
10100 if (mol->mview == NULL)
10101 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10102 index = NUM2INT(rb_Integer(gval));
10103 if (index < 0 || index >= mol->mview->ngraphics)
10104 rb_raise(rb_eArgError, "the graphic index is out of range");
10105 gp = mol->mview->graphics + index;
10107 MoleculeCallback_notifyModification(mol, 0);
10113 * hide_graphic(graphic_index) -> self
10115 * Disable the visible flag of the graphic_index-th graphic object
10119 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10121 MainViewGraphic *gp;
10124 Data_Get_Struct(self, Molecule, mol);
10125 if (mol->mview == NULL)
10126 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10127 index = NUM2INT(rb_Integer(gval));
10128 if (index < 0 || index >= mol->mview->ngraphics)
10129 rb_raise(rb_eArgError, "the graphic index is out of range");
10130 gp = mol->mview->graphics + index;
10132 MoleculeCallback_notifyModification(mol, 0);
10138 * show_text(string)
10140 * Show the string in the info text box.
10143 s_Molecule_ShowText(VALUE self, VALUE arg)
10146 Data_Get_Struct(self, Molecule, mol);
10147 if (mol->mview != NULL)
10148 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10152 #pragma mark ------ MD Support ------
10156 * md_arena -> MDArena
10158 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10159 * this molecule, a new arena is created.
10162 s_Molecule_MDArena(VALUE self)
10166 Data_Get_Struct(self, Molecule, mol);
10167 if (mol->arena == NULL)
10169 retval = ValueFromMDArena(mol->arena);
10175 * set_parameter_attr(type, index, key, value, src) -> value
10177 * This method is used only internally.
10180 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10182 /* This method is called from MolAction to change a MM parameter attribute. */
10185 ParameterRef *pref;
10187 Data_Get_Struct(self, Molecule, mol);
10188 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10189 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10191 /* This is the special part of this method; it allows modification of the src field. */
10192 /* (ParameterRef#set_attr sets 0 to the src field) */
10193 Data_Get_Struct(pval, ParameterRef, pref);
10194 up = ParameterRefGetPar(pref);
10195 up->bond.src = FIX2INT(sval);
10202 * parameter -> Parameter
10204 * Get the local parameter of this molecule. If not defined, returns nil.
10207 s_Molecule_Parameter(VALUE self)
10210 Data_Get_Struct(self, Molecule, mol);
10211 /* if (mol->par == NULL)
10213 return s_NewParameterValueFromValue(self);
10218 * start_step -> Integer
10220 * Returns the start step (defined by dcd format).
10223 s_Molecule_StartStep(VALUE self)
10226 Data_Get_Struct(self, Molecule, mol);
10227 return INT2NUM(mol->startStep);
10232 * start_step = Integer
10234 * Set the start step (defined by dcd format).
10237 s_Molecule_SetStartStep(VALUE self, VALUE val)
10240 Data_Get_Struct(self, Molecule, mol);
10241 mol->startStep = NUM2INT(rb_Integer(val));
10247 * steps_per_frame -> Integer
10249 * Returns the number of steps between frames (defined by dcd format).
10252 s_Molecule_StepsPerFrame(VALUE self)
10255 Data_Get_Struct(self, Molecule, mol);
10256 return INT2NUM(mol->stepsPerFrame);
10261 * steps_per_frame = Integer
10263 * Set the number of steps between frames (defined by dcd format).
10266 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10269 Data_Get_Struct(self, Molecule, mol);
10270 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10276 * ps_per_step -> Float
10278 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10281 s_Molecule_PsPerStep(VALUE self)
10284 Data_Get_Struct(self, Molecule, mol);
10285 return rb_float_new(mol->psPerStep);
10290 * ps_per_step = Float
10292 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10295 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10298 Data_Get_Struct(self, Molecule, mol);
10299 mol->psPerStep = NUM2DBL(rb_Float(val));
10304 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10306 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.");
10309 #pragma mark ------ MO Handling ------
10313 * selectedMO -> IntGroup
10315 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10316 * is not selected, returns nil. If the MO info table is selected but no MOs
10317 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10320 s_Molecule_SelectedMO(VALUE self)
10325 Data_Get_Struct(self, Molecule, mol);
10326 if (mol->mview == NULL)
10328 ig = MainView_selectedMO(mol->mview);
10331 IntGroupOffset(ig, 1);
10332 val = ValueFromIntGroup(ig);
10333 IntGroupRelease(ig);
10339 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10341 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10342 * If the molecule does not contain a basis set information, then returns nil.
10345 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10348 Vector o, dx, dy, dz;
10351 Int npoints = 80 * 80 * 80;
10352 Data_Get_Struct(self, Molecule, mol);
10353 if (mol->bset == NULL)
10355 rb_scan_args(argc, argv, "01", &nval);
10357 npoints = NUM2INT(rb_Integer(nval));
10358 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10360 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));
10364 s_Cubegen_callback(double progress, void *ref)
10366 MyAppCallback_setProgressValue(progress);
10367 if (MyAppCallback_checkInterrupt())
10374 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10375 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10377 * Calculate the molecular orbital with number mo and create a 'cube' file.
10378 * In the first form, the cube size is estimated from the atomic coordinates. In the
10379 * second form, the cube dimension is explicitly given.
10380 * Returns fname when successful, nil otherwise.
10381 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10382 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10383 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10386 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10388 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10390 Int mono, nx, ny, nz, npoints;
10391 Vector o, dx, dy, dz;
10394 Data_Get_Struct(self, Molecule, mol);
10395 if (mol->bset == NULL)
10396 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10397 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10399 /* Set up parameters */
10400 mono = NUM2INT(rb_Integer(mval));
10401 if (mono < 0 || mono > mol->bset->ncomps)
10402 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);
10404 if (mol->bset->rflag != 0)
10405 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10406 mono += mol->bset->ncomps;
10409 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10410 /* Automatic grid formation */
10412 npoints = NUM2INT(rb_Integer(oval));
10416 else if (npoints < 8)
10417 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10418 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10419 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10423 VectorFromValue(oval, &o);
10424 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10425 VectorFromValue(dxval, &dx);
10427 dx.x = NUM2DBL(rb_Float(dxval));
10430 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10431 VectorFromValue(dyval, &dy);
10433 dy.y = NUM2DBL(rb_Float(dyval));
10436 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10437 VectorFromValue(dzval, &dz);
10439 dz.z = NUM2DBL(rb_Float(dzval));
10442 nx = NUM2INT(rb_Integer(nxval));
10443 ny = NUM2INT(rb_Integer(nyval));
10444 nz = NUM2INT(rb_Integer(nzval));
10445 if (nx <= 0 || ny <= 0 || nz <= 0)
10446 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10447 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10448 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);
10452 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10455 else if (index < 0)
10456 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10458 /* Output to file */
10459 MoleculeCallback_displayName(mol, buf, sizeof buf);
10460 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10462 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10464 /* Discard the cube */
10465 MoleculeClearCubeAtIndex(mol, index);
10473 * Clear the MO surface if present.
10476 s_Molecule_ClearSurface(VALUE self)
10479 Data_Get_Struct(self, Molecule, mol);
10480 if (mol->mcube != NULL)
10481 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10489 * Hide the MO surface if present.
10492 s_Molecule_HideSurface(VALUE self)
10495 Data_Get_Struct(self, Molecule, mol);
10496 if (mol->mcube != NULL) {
10497 mol->mcube->hidden = 1;
10498 MoleculeCallback_notifyModification(mol, 0);
10507 * Show the MO surface if present.
10510 s_Molecule_ShowSurface(VALUE self)
10513 Data_Get_Struct(self, Molecule, mol);
10514 if (mol->mcube != NULL) {
10515 mol->mcube->hidden = 0;
10516 MoleculeCallback_notifyModification(mol, 0);
10523 * create_surface(mo, attr = nil)
10525 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10526 * then it denotes the beta orbital.
10527 * If mo is nil, then the attributes of the current surface are modified.
10529 * :npoints : the approximate number of grid points
10530 * :expand : the scale factor to expand/shrink the display box size for each atom,
10531 * :thres : the threshold for the isovalue surface
10532 * If the molecule does not contain MO information, raises exception.
10535 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10538 Vector o, dx, dy, dz;
10539 Int nmo, nx, ny, nz, i;
10540 Int need_recalc = 0;
10541 VALUE nval, hval, aval;
10546 Data_Get_Struct(self, Molecule, mol);
10547 rb_scan_args(argc, argv, "11", &nval, &hval);
10548 if (mol->bset == NULL)
10549 rb_raise(rb_eMolbyError, "No MO information is given");
10550 if (nval == Qnil) {
10552 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10553 nmo = mol->bset->nmos + 1;
10555 nmo = NUM2INT(rb_Integer(nval));
10556 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10557 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);
10559 nmo = -nmo + mol->bset->ncomps;
10561 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10562 npoints = NUM2INT(rb_Integer(aval));
10564 } else if (mol->mcube != NULL) {
10565 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10566 } else npoints = 80 * 80 * 80;
10567 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10568 expand = NUM2DBL(rb_Float(aval));
10569 } else if (mol->mcube != NULL) {
10570 expand = mol->mcube->expand;
10571 } else expand = 1.0;
10572 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10573 thres = NUM2DBL(rb_Float(aval));
10574 } else if (mol->mcube != NULL) {
10575 thres = mol->mcube->thres;
10576 } else thres = 0.05;
10577 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10578 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10579 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10580 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10581 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10583 for (nx = 0; nx < 2; nx++) {
10584 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10585 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10586 aval = rb_ary_to_ary(aval);
10587 if (RARRAY_LEN(aval) < 3) {
10589 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10591 for (i = 0; i < 4; i++)
10592 d[i] = mol->mcube->c[nx].rgba[i];
10593 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10594 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10595 if (d[i] < 0.0 && d[i] > 1.0)
10598 for (i = 0; i < 4; i++)
10599 mol->mcube->c[nx].rgba[i] = d[i];
10602 if (mol->mcube->expand != expand)
10604 mol->mcube->thres = thres;
10605 mol->mcube->expand = expand;
10607 if (mol->mcube->idn < 0)
10608 return self; /* Only set attributes for now */
10610 nmo = mol->mcube->idn; /* Force recalculation */
10612 if (MoleculeUpdateMCube(mol, nmo) != 0)
10613 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10619 * set_surface_attr(attr = nil)
10621 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10624 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10629 return s_Molecule_CreateSurface(2, args, self);
10636 * Get the number of electrostatic potential info.
10639 s_Molecule_NElpots(VALUE self)
10642 Data_Get_Struct(self, Molecule, mol);
10643 return INT2NUM(mol->nelpots);
10650 * Get the electrostatic potential info at the given index. If present, then the
10651 * return value is [Vector, Float] (position and potential). If not present, then
10655 s_Molecule_Elpot(VALUE self, VALUE ival)
10659 Data_Get_Struct(self, Molecule, mol);
10660 idx = NUM2INT(rb_Integer(ival));
10661 if (idx < 0 || idx >= mol->nelpots)
10663 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10670 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10671 * cube and marching cube information are discarded. This operation is _not_ undoable!
10674 s_Molecule_ClearBasisSet(VALUE self)
10677 Data_Get_Struct(self, Molecule, mol);
10679 if (mol->bset != NULL) {
10680 BasisSetRelease(mol->bset);
10683 if (mol->mcube != NULL) {
10684 MoleculeDeallocateMCube(mol->mcube);
10693 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives[, additional_exponent])
10695 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10696 * and the number of primitives.
10697 * Additional exponent is for JANPA only; implements an additinal r^N component that
10698 * appears in cartesian->spherical conversion.
10699 * Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type; -2, D5-type;
10700 * 3, F-type; -3, F7-type; 4, G-type; -4, G9-type.
10701 * Or: "s", S-type; "p", P-type; "sp", SP-type; "d", D-type; "d5", D5-type;
10702 * "f", F-type; "f7", F7-type; "g", G-type; "g9", G9-type
10705 s_Molecule_AddGaussianOrbitalShell(int argc, VALUE *argv, VALUE self)
10708 int sym, nprims, a_idx, n, add_exp;
10709 VALUE aval, symval, npval, addval;
10710 Data_Get_Struct(self, Molecule, mol);
10711 rb_scan_args(argc, argv, "31", &aval, &symval, &npval, &addval);
10712 if (rb_obj_is_kind_of(symval, rb_cString)) {
10713 const char *p = StringValuePtr(symval);
10714 if (strcasecmp(p, "s") == 0)
10716 else if (strcasecmp(p, "p") == 0)
10718 else if (strcasecmp(p, "sp") == 0)
10720 else if (strcasecmp(p, "d") == 0)
10722 else if (strcasecmp(p, "d5") == 0)
10724 else if (strcasecmp(p, "f") == 0)
10726 else if (strcasecmp(p, "f7") == 0)
10728 else if (strcasecmp(p, "g") == 0)
10730 else if (strcasecmp(p, "g9") == 0)
10733 rb_raise(rb_eArgError, "Unknown orbital type '%s'", p);
10735 sym = NUM2INT(rb_Integer(symval));
10737 a_idx = NUM2INT(rb_Integer(aval));
10738 nprims = NUM2INT(rb_Integer(npval));
10739 if (addval != Qnil)
10740 add_exp = NUM2INT(rb_Integer(addval));
10742 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims, add_exp);
10744 rb_raise(rb_eMolbyError, "Molecule is emptry");
10746 rb_raise(rb_eMolbyError, "Low memory");
10748 rb_raise(rb_eMolbyError, "Unknown orbital type");
10750 rb_raise(rb_eMolbyError, "Unknown error");
10756 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10758 * To be used internally. Add a gaussian primitive coefficients.
10761 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10765 Double exponent, contraction, contraction_sp;
10766 Data_Get_Struct(self, Molecule, mol);
10767 exponent = NUM2DBL(rb_Float(expval));
10768 contraction = NUM2DBL(rb_Float(cval));
10769 contraction_sp = NUM2DBL(rb_Float(cspval));
10770 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10772 rb_raise(rb_eMolbyError, "Molecule is emptry");
10774 rb_raise(rb_eMolbyError, "Low memory");
10776 rb_raise(rb_eMolbyError, "Unknown error");
10782 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10784 * Get the Gaussian shell information for the given MO coefficient index.
10785 * The symmetry code is the same as in add_gaussian_orbital_shell.
10786 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10787 * is the number of MO component belonging to this shell.
10790 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10795 Data_Get_Struct(self, Molecule, mol);
10796 if (mol->bset == NULL)
10797 rb_raise(rb_eMolbyError, "No basis set information is defined");
10798 s_idx = NUM2INT(rb_Integer(sval));
10799 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10801 sp = mol->bset->shells + s_idx;
10804 case kGTOType_S: sym = 0; break;
10805 case kGTOType_SP: sym = -1; break;
10806 case kGTOType_P: sym = 1; break;
10807 case kGTOType_D: sym = 2; break;
10808 case kGTOType_D5: sym = -2; break;
10809 case kGTOType_F: sym = 3; break;
10810 case kGTOType_F7: sym = -3; break;
10811 case kGTOType_G: sym = 4; break;
10812 case kGTOType_G9: sym = -4; break;
10814 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10816 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10821 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10823 * Get the Gaussian primitive coefficients for the given MO component.
10826 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10832 VALUE retval, aval;
10833 Data_Get_Struct(self, Molecule, mol);
10834 if (mol->bset == NULL)
10835 rb_raise(rb_eMolbyError, "No basis set information is defined");
10836 s_idx = NUM2INT(rb_Integer(sval));
10837 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10839 sp = mol->bset->shells + s_idx;
10840 pp = mol->bset->priminfos + sp->p_idx;
10841 retval = rb_ary_new2(sp->nprim);
10842 for (i = 0; i < sp->nprim; i++) {
10843 if (sp->sym == kGTOType_SP) {
10844 /* With P contraction coefficient */
10845 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10847 /* Without P contraction coefficient */
10848 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10850 rb_ary_store(retval, i, aval);
10857 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10859 * Get the Gaussian shell information for the given MO coefficient index.
10862 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10865 Int n, c, atom_idx, shell_idx;
10867 Data_Get_Struct(self, Molecule, mol);
10868 if (mol->bset == NULL)
10869 rb_raise(rb_eMolbyError, "No basis set information is defined");
10870 c = NUM2INT(rb_Integer(cval));
10871 if (c < 0 || c >= mol->bset->ncomps)
10873 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10875 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10876 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10881 * clear_mo_coefficients
10883 * Clear the existing MO coefficients.
10886 s_Molecule_ClearMOCoefficients(VALUE self)
10889 Data_Get_Struct(self, Molecule, mol);
10890 if (mol->bset != NULL) {
10891 if (mol->bset->moenergies != NULL) {
10892 free(mol->bset->moenergies);
10893 mol->bset->moenergies = NULL;
10895 if (mol->bset->mo != NULL) {
10896 free(mol->bset->mo);
10897 mol->bset->mo = NULL;
10899 mol->bset->nmos = 0;
10906 * set_mo_coefficients(idx, energy, coefficients)
10908 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10909 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10910 * Energy is the MO energy, and coefficients is an array
10911 * of MO coefficients.
10914 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10917 Int idx, ncomps, i;
10920 Data_Get_Struct(self, Molecule, mol);
10921 idx = NUM2INT(rb_Integer(ival));
10922 energy = NUM2DBL(rb_Float(eval));
10923 aval = rb_ary_to_ary(aval);
10924 ncomps = RARRAY_LEN(aval);
10925 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10926 if (coeffs == NULL) {
10930 for (i = 0; i < ncomps; i++)
10931 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10932 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10935 rb_raise(rb_eMolbyError, "Molecule is emptry");
10937 rb_raise(rb_eMolbyError, "Low memory");
10939 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10941 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10943 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10945 rb_raise(rb_eMolbyError, "Unknown error");
10951 * get_mo_coefficients(idx)
10953 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10956 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10959 Int idx, ncomps, n;
10963 Data_Get_Struct(self, Molecule, mol);
10964 idx = NUM2INT(rb_Integer(ival));
10967 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10969 rb_raise(rb_eMolbyError, "Molecule is emptry");
10971 rb_raise(rb_eMolbyError, "No basis set information is present");
10973 return Qnil; /* Silently returns nil */
10974 retval = rb_ary_new2(ncomps);
10975 for (n = 0; n < ncomps; n++)
10976 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10983 * get_mo_energy(idx)
10985 * To be used internally. Get the MO energy for the given MO index (1-based).
10988 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10993 Data_Get_Struct(self, Molecule, mol);
10994 idx = NUM2INT(rb_Integer(ival));
10995 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10997 rb_raise(rb_eMolbyError, "Molecule is emptry");
10999 rb_raise(rb_eMolbyError, "No basis set information is present");
11002 return rb_float_new(energy);
11005 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
11008 s_InitMOInfoKeys(void)
11010 if (sTypeSym == 0) {
11011 sTypeSym = ID2SYM(rb_intern("type"));
11012 sAlphaSym = ID2SYM(rb_intern("alpha"));
11013 sBetaSym = ID2SYM(rb_intern("beta"));
11014 sNcompsSym = ID2SYM(rb_intern("ncomps"));
11015 sNshellsSym = ID2SYM(rb_intern("nshells"));
11021 * set_mo_info(hash)
11023 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
11024 * :alpha=>integer, :beta=>integer
11027 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
11031 Int rflag, na, nb, n;
11033 Data_Get_Struct(self, Molecule, mol);
11034 if (mol->bset != NULL) {
11035 rflag = mol->bset->rflag;
11036 na = mol->bset->ne_alpha;
11037 nb = mol->bset->ne_beta;
11043 if (hval != Qnil) {
11044 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
11045 s = StringValuePtr(aval);
11046 if (strcasecmp(s, "RHF") == 0)
11048 else if (strcasecmp(s, "UHF") == 0)
11050 else if (strcasecmp(s, "ROHF") == 0)
11053 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
11054 n = NUM2INT(rb_Integer(aval));
11058 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11059 n = NUM2INT(rb_Integer(aval));
11063 MoleculeSetMOInfo(mol, rflag, na, nb);
11072 * Get the MO info. The key is as described in set_mo_info.
11073 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11076 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11079 Data_Get_Struct(self, Molecule, mol);
11080 if (mol->bset == NULL)
11082 if (kval == sTypeSym) {
11083 switch (mol->bset->rflag) {
11084 case 0: return Ruby_NewEncodedStringValue2("UHF");
11085 case 1: return Ruby_NewEncodedStringValue2("RHF");
11086 case 2: return Ruby_NewEncodedStringValue2("ROHF");
11087 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11089 } else if (kval == sAlphaSym) {
11090 return INT2NUM(mol->bset->ne_alpha);
11091 } else if (kval == sBetaSym) {
11092 return INT2NUM(mol->bset->ne_beta);
11093 } else if (kval == sNcompsSym) {
11094 return INT2NUM(mol->bset->ncomps);
11095 } else if (kval == sNshellsSym) {
11096 return INT2NUM(mol->bset->nshells);
11098 kval = rb_inspect(kval);
11099 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11100 return Qnil; /* Does not reach here */
11108 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11111 s_Molecule_MOType(VALUE self)
11113 return s_Molecule_GetMOInfo(self, sTypeSym);
11116 #pragma mark ------ Molecular Topology ------
11120 * search_equivalent_atoms(ig = nil)
11122 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
11125 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11131 Data_Get_Struct(self, Molecule, mol);
11132 if (mol->natoms == 0)
11134 rb_scan_args(argc, argv, "01", &val);
11136 ig = s_Molecule_AtomGroupFromValue(self, val);
11138 result = MoleculeSearchEquivalentAtoms(mol, ig);
11139 if (result == NULL)
11140 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11142 IntGroupRelease(ig);
11143 val = rb_ary_new2(mol->natoms);
11144 for (i = 0; i < mol->natoms; i++)
11145 rb_ary_push(val, INT2NUM(result[i]));
11152 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11154 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11155 * Name is the name of the new pi anchor, and group is the atoms that define
11156 * the pi system. Type (a String) is an atom type for MM implementation.
11157 * Weights represent the relative significance of the component atoms; if omitted, then
11158 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
11159 * The weight values will be normalized so that the sum of the weights is 1.0.
11160 * The weight values must be positive.
11161 * Index is the atom index where the created pi-anchor is inserted in the
11162 * atoms array; if omitted, the pi-anchor is inserted after the component atom
11163 * having the largest index.
11164 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
11165 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11168 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11173 Int i, n, idx, last_component;
11177 if (argc < 2 || argc >= 6)
11178 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11182 Data_Get_Struct(self, Molecule, mol);
11186 ig = s_Molecule_AtomGroupFromValue(self, gval);
11187 if (ig == NULL || IntGroupGetCount(ig) == 0)
11188 rb_raise(rb_eMolbyError, "atom group is not given correctly");
11189 memset(&a, 0, sizeof(a));
11190 memset(&an, 0, sizeof(an));
11191 strncpy(a.aname, StringValuePtr(nval), 4);
11192 if (a.aname[0] == '_')
11193 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11194 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11195 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11196 if (n >= mol->natoms) {
11197 AtomConnectResize(&an.connect, 0);
11198 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11200 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11201 last_component = n;
11203 if (an.connect.count == 0)
11204 rb_raise(rb_eMolbyError, "no atoms are specified");
11205 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11206 for (i = 0; i < an.connect.count; i++) {
11207 an.coeffs[i] = 1.0 / an.connect.count;
11209 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11211 if (argv[0] != Qnil)
11212 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11216 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11217 if (argv[0] != Qnil) {
11218 VALUE aval = rb_ary_to_ary(argv[0]);
11220 if (RARRAY_LEN(aval) != an.connect.count)
11221 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11222 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11223 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11225 rb_raise(rb_eMolbyError, "the weight value must be positive");
11229 for (i = 0; i < an.connect.count; i++)
11230 an.coeffs[i] /= sum;
11235 if (argc > 0 && argv[0] != Qnil) {
11237 idx = NUM2INT(rb_Integer(argv[0]));
11239 if (idx < 0 || idx > mol->natoms) {
11240 /* Immediately after the last specified atom */
11241 idx = last_component + 1;
11243 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11244 memmove(a.anchor, &an, sizeof(PiAnchor));
11245 /* Use residue information of the last specified atom */
11246 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11247 a.resSeq = ap->resSeq;
11248 strncpy(a.resName, ap->resName, 4);
11249 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11251 MoleculeCalculatePiAnchorPosition(mol, idx);
11252 aref = AtomRefNew(mol, idx);
11253 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11256 #pragma mark ------ Molecular Properties ------
11260 * set_property(name, value[, index]) -> value
11261 * set_property(name, values, group) -> values
11263 * Set molecular property. A property is a floating-point number with a specified name,
11264 * and can be set for each frame separately. The name of the property is given as a String.
11265 * The value can be a single floating point number, which is set to the current frame.
11269 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11272 VALUE nval, vval, ival;
11275 Int i, n, idx, fidx;
11277 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11278 Data_Get_Struct(self, Molecule, mol);
11279 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11280 idx = NUM2INT(rb_Integer(nval));
11281 if (idx < 0 || idx >= mol->nmolprops)
11282 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11284 name = StringValuePtr(nval);
11285 idx = MoleculeLookUpProperty(mol, name);
11287 idx = MoleculeCreateProperty(mol, name);
11289 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11292 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11294 fidx = mol->cframe;
11296 fidx = NUM2INT(rb_Integer(ival));
11297 n = MoleculeGetNumberOfFrames(mol);
11298 if (fidx < 0 || fidx >= n)
11299 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11301 ig = IntGroupNewWithPoints(fidx, 1, -1);
11302 dp = (Double *)malloc(sizeof(Double));
11303 *dp = NUM2DBL(rb_Float(vval));
11306 vval = rb_ary_to_ary(vval);
11307 ig = IntGroupFromValue(ival);
11308 n = IntGroupGetCount(ig);
11310 rb_raise(rb_eMolbyError, "No frames are specified");
11311 if (RARRAY_LEN(vval) < n)
11312 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11313 dp = (Double *)calloc(sizeof(Double), n);
11314 for (i = 0; i < n; i++)
11315 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11318 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11320 IntGroupRelease(ig);
11326 * get_property(name[, index]) -> value
11327 * get_property(name, group) -> values
11329 * Get molecular property. In the first form, a property value for a single frame is returned.
11330 * (If index is omitted, then the value for the current frame is given)
11331 * In the second form, an array of property values for the given frames is returned.
11332 * If name is not one of known properties or a valid index integer, exception is raised.
11335 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11341 Int i, n, idx, fidx;
11343 rb_scan_args(argc, argv, "11", &nval, &ival);
11344 Data_Get_Struct(self, Molecule, mol);
11345 if (mol->nmolprops == 0)
11346 rb_raise(rb_eMolbyError, "The molecule has no properties");
11347 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11348 idx = NUM2INT(rb_Integer(nval));
11349 if (idx < 0 || idx >= mol->nmolprops)
11350 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11352 name = StringValuePtr(nval);
11353 idx = MoleculeLookUpProperty(mol, name);
11355 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11357 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11359 fidx = mol->cframe;
11361 fidx = NUM2INT(rb_Integer(ival));
11362 n = MoleculeGetNumberOfFrames(mol);
11363 if (fidx < 0 || fidx >= n)
11364 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11366 ig = IntGroupNewWithPoints(fidx, 1, -1);
11367 ival = INT2FIX(fidx);
11370 ig = IntGroupFromValue(ival);
11371 n = IntGroupGetCount(ig);
11373 return rb_ary_new();
11375 dp = (Double *)calloc(sizeof(Double), n);
11376 MoleculeGetProperty(mol, idx, ig, dp);
11377 if (FIXNUM_P(ival))
11378 ival = rb_float_new(dp[0]);
11380 ival = rb_ary_new();
11381 for (i = n - 1; i >= 0; i--) {
11382 nval = rb_float_new(dp[i]);
11383 rb_ary_store(ival, i, nval);
11387 IntGroupRelease(ig);
11393 * property_names -> Array
11395 * Get an array of property names.
11398 s_Molecule_PropertyNames(VALUE self)
11403 Data_Get_Struct(self, Molecule, mol);
11404 rval = rb_ary_new();
11405 for (i = mol->nmolprops - 1; i >= 0; i--) {
11406 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11407 rb_ary_store(rval, i, nval);
11412 #pragma mark ------ Class methods ------
11416 * current -> Molecule
11418 * Get the currently "active" molecule.
11421 s_Molecule_Current(VALUE klass)
11423 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11428 * Molecule[] -> Molecule
11429 * Molecule[n] -> Molecule
11430 * Molecule[name] -> Molecule
11431 * Molecule[name, k] -> Molecule
11432 * Molecule[regex] -> Molecule
11433 * Molecule[regex, k] -> Molecule
11435 * Molecule[] is equivalent to Molecule.current.
11436 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11437 * Molecule[name] gives the first document (in the order of creation time) that has
11438 * the given name. If a second argument (k) is given, the k-th document that has the
11439 * given name is returned.
11440 * Molecule[regex] gives the first document (in the order of creation time) that
11441 * has a name matching the regular expression. If a second argument (k) is given,
11442 * the k-th document that has a name matching the re is returned.
11445 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11451 rb_scan_args(argc, argv, "02", &val, &kval);
11453 return s_Molecule_Current(klass);
11454 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11455 idx = NUM2INT(val);
11456 mol = MoleculeCallback_moleculeAtIndex(idx);
11457 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11458 char *p = StringValuePtr(val);
11459 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11460 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11461 MoleculeCallback_displayName(mol, buf, sizeof buf);
11462 if (strcmp(buf, p) == 0 && --k == 0)
11465 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11466 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11467 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11469 MoleculeCallback_displayName(mol, buf, sizeof buf);
11470 name = Ruby_NewEncodedStringValue2(buf);
11471 if (rb_reg_match(val, name) != Qnil && --k == 0)
11474 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11478 else return ValueFromMolecule(mol);
11483 * list -> array of Molecules
11485 * Get the list of molecules associated to the documents, in the order of creation
11486 * time of the document. If no document is open, returns an empry array.
11489 s_Molecule_List(VALUE klass)
11495 ary = rb_ary_new();
11496 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11497 rb_ary_push(ary, ValueFromMolecule(mol));
11505 * ordered_list -> array of Molecules
11507 * Get the list of molecules associated to the documents, in the order of front-to-back
11508 * ordering of the associated window. If no document is open, returns an empry array.
11511 s_Molecule_OrderedList(VALUE klass)
11517 ary = rb_ary_new();
11518 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11519 rb_ary_push(ary, ValueFromMolecule(mol));
11525 #pragma mark ------ Call Subprocess ------
11527 /* The callback functions for call_subprocess_async */
11529 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11532 VALUE procval, retval, args[2];
11533 args[0] = ValueFromMolecule(mol);
11534 args[1] = INT2NUM(status);
11535 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11536 if (procval != Qnil) {
11537 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11538 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11545 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11548 VALUE procval, retval, args[2];
11549 args[0] = ValueFromMolecule(mol);
11550 args[1] = INT2NUM(tcount);
11551 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11552 if (procval != Qnil) {
11553 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11554 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11562 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11564 * Call subprocess asynchronically.
11565 * cmd is either a single string of an array of string. If it is a single string, then
11566 * it will be given to wxExecute as a single argument. In this case, the string can be
11567 * split into arguments by whitespace. If this behavior is not intended, then use an array
11568 * containing a single string.
11569 * If end_callback is given, it will be called (with two arguments self and termination status)
11570 * when the subprocess terminated.
11571 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11572 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11573 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11574 * filename begins with ">>", then the message will be appended to the file.
11575 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11576 * If the argument is nil, then the message will be sent to the Ruby console.
11577 * Returns the process ID as an integer.
11580 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11582 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11585 const char **cmdargv;
11587 FILE *fpout, *fperr;
11588 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11589 Data_Get_Struct(self, Molecule, mol);
11591 if (stdout_val == Qnil) {
11594 sout = StringValuePtr(stdout_val);
11595 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11598 if (strncmp(sout, ">>", 2) == 0) {
11600 fpout = fopen(sout, "a");
11604 fpout = fopen(sout, "w");
11607 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11610 if (stderr_val == Qnil) {
11613 serr = StringValuePtr(stderr_val);
11614 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11617 if (strncmp(serr, ">>", 2) == 0) {
11619 fpout = fopen(serr, "a");
11623 fperr = fopen(serr, "w");
11626 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11630 /* Register procs as instance variables */
11631 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11632 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11634 if (rb_obj_is_kind_of(cmd, rb_cString)) {
11635 cmdargv = calloc(sizeof(cmdargv[0]), 3);
11636 cmdargv[0] = StringValuePtr(cmd);
11640 cmd = rb_ary_to_ary(cmd);
11641 cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
11642 for (n = 0; n < RARRAY_LEN(cmd); n++) {
11643 cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
11647 n = MoleculeCallback_callSubProcessAsync(mol, cmdargv, s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11648 if (fpout != NULL && fpout != (FILE *)1)
11650 if (fperr != NULL && fperr != (FILE *)1)
11655 #pragma mark ====== Define Molby Classes ======
11662 /* Define module Molby */
11663 rb_mMolby = rb_define_module("Molby");
11665 /* Define Vector3D, Transform, IntGroup */
11668 /* Define MDArena */
11669 Init_MolbyMDTypes();
11671 /* class Molecule */
11672 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11674 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11675 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11676 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11677 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11678 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11680 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11681 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11682 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11683 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11684 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11685 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11686 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11687 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11688 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11689 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11690 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11691 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11692 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11693 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11694 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11695 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11696 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11697 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11698 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11699 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11700 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11701 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11703 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11704 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11705 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11706 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11707 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11709 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11710 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11711 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11712 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11713 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11714 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11715 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11716 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11717 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11718 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11719 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11720 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11721 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11722 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11723 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11725 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11726 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11727 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11728 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11729 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11731 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11732 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11733 rb_define_alias(rb_cMolecule, "+", "add");
11734 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11735 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11736 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11737 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11738 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11739 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11740 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11741 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11742 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11743 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11744 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11745 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11746 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11747 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11748 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11749 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11750 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11751 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11752 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11753 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11754 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11756 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11757 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11758 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11759 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11760 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11762 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11763 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11764 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11765 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11766 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11767 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11768 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11769 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11770 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11772 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11773 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11774 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11775 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11776 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11777 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11778 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11779 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11780 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11781 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11782 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11783 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11784 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11785 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11786 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11787 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11788 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11789 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11790 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11791 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11792 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11793 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11795 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11796 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11797 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11798 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11799 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11800 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11801 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11803 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11804 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11805 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11806 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11807 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11808 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11809 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11810 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11811 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11812 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11813 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11814 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11815 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11817 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11818 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11819 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11820 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11821 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11822 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11824 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11825 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11826 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11827 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11828 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11829 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11830 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11831 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11832 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11833 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11834 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11835 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11836 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11837 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11838 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11839 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11840 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11841 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11842 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11843 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11844 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11845 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11846 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11847 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11848 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11849 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11850 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11851 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11852 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11853 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11854 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11855 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11856 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11857 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11858 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11859 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11860 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11861 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11862 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11863 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11864 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11865 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11866 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11867 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11868 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11869 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11870 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11871 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11872 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11873 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11874 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11875 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11876 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11877 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11879 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11880 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11881 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11882 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11883 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11884 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11885 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11886 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11887 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11888 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11889 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11890 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11891 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11893 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11894 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11895 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11896 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11897 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11898 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11899 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11900 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11901 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11902 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11903 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11904 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11905 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11906 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11908 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11909 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11910 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11911 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11912 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11913 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11914 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11915 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11916 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11917 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11918 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11919 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, -1);
11920 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11921 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11922 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11923 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11924 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11925 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11926 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11927 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11928 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11929 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11930 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11932 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11933 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11935 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11936 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11937 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11939 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11940 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11941 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11942 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11944 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11946 /* class MolEnumerable */
11947 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11948 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11949 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11950 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11951 rb_define_alias(rb_cMolEnumerable, "size", "length");
11952 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11953 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11955 /* class AtomRef */
11956 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11957 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11959 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11960 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11961 s_AtomAttrDefTable[i].id = rb_intern(buf);
11962 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11964 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11966 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11967 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11968 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11969 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11970 s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11971 rb_global_variable(&s_SetAtomAttrString);
11972 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11973 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11975 /* class Parameter */
11976 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11977 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11978 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11979 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11980 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11981 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11982 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11983 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11984 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11985 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11986 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11987 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11988 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11989 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11990 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11991 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11992 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11993 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11994 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11995 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11996 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11997 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11998 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
11999 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12000 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12001 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12002 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
12003 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
12004 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
12005 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
12006 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
12007 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
12008 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
12009 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
12010 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
12011 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
12012 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
12013 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
12014 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
12015 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
12016 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
12017 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
12018 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
12019 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
12020 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
12021 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
12022 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
12023 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
12024 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
12025 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12026 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12027 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12028 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12029 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
12031 /* class ParEnumerable */
12032 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
12033 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
12034 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
12035 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
12036 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
12037 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
12038 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
12039 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
12040 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
12041 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
12042 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
12044 /* class ParameterRef */
12045 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
12046 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
12048 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
12049 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
12050 s_ParameterAttrDefTable[i].id = rb_intern(buf);
12051 if (s_ParameterAttrDefTable[i].symref != NULL)
12052 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
12053 if (s_ParameterAttrDefTable[i].setter != NULL) {
12055 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
12058 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
12059 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
12060 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
12061 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
12062 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
12063 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
12064 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
12065 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
12067 /* class MolbyError */
12068 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
12070 /* module Kernel */
12071 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
12072 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
12073 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
12074 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12075 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12076 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12077 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12078 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12079 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12080 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12081 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12082 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12083 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12084 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12085 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12086 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12087 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12088 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12089 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12090 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12091 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12092 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12093 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12094 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12095 rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12096 rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12097 rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12098 rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12099 rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12100 rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12103 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12105 s_ID_equal = rb_intern("==");
12106 g_RubyID_call = rb_intern("call");
12108 s_InitMOInfoKeys();
12110 /* Symbols for graphics */
12111 s_LineSym = ID2SYM(rb_intern("line"));
12112 s_PolySym = ID2SYM(rb_intern("poly"));
12113 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12114 s_ConeSym = ID2SYM(rb_intern("cone"));
12115 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12118 #pragma mark ====== Interface with RubyDialog class ======
12121 RubyDialogCallback_parentModule(void)
12123 return (RubyValue)rb_mMolby;
12126 #pragma mark ====== External functions ======
12128 static VALUE s_ruby_top_self = Qfalse;
12129 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12130 static VALUE s_ruby_export_local_variables = Qfalse;
12133 s_evalRubyScriptOnMoleculeSub(VALUE val)
12135 void **ptr = (void **)val;
12136 Molecule *mol = (Molecule *)ptr[1];
12137 VALUE sval, fnval, lnval, retval;
12140 /* Clear the error information (store in the history array if necessary) */
12141 sval = rb_errinfo();
12142 if (sval != Qnil) {
12143 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12144 rb_set_errinfo(Qnil);
12147 if (s_ruby_top_self == Qfalse) {
12148 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12150 if (s_ruby_get_binding_for_molecule == Qfalse) {
12152 "lambda { |_mol_, _bind_| \n"
12153 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12154 " _proc_.call(_mol_) } ";
12155 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12156 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12158 if (s_ruby_export_local_variables == Qfalse) {
12160 "lambda { |_bind_| \n"
12161 " # find local variables newly defined in _bind_ \n"
12162 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12163 " _a_.each { |_vsym_| \n"
12164 " _vname_ = _vsym_.to_s \n"
12165 " _vval_ = _bind_.eval(_vname_) \n"
12166 " # Define local variable \n"
12167 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12168 " # Then set value \n"
12169 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12172 s_ruby_export_local_variables = rb_eval_string(s2);
12173 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12175 if (ptr[2] == NULL) {
12177 /* String literal: we need to specify string encoding */
12178 #if defined(__WXMSW__)
12179 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12181 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12183 sval = Ruby_NewEncodedStringValue2(scr);
12185 fnval = Ruby_NewEncodedStringValue2("(eval)");
12186 lnval = INT2FIX(0);
12188 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12189 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12190 lnval = INT2FIX(1);
12192 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12194 VALUE mval = ValueFromMolecule(mol);
12195 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12197 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12199 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12205 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12209 VALUE save_interrupt_flag;
12210 /* char *save_ruby_sourcefile;
12211 int save_ruby_sourceline; */
12212 if (gMolbyIsCheckingInterrupt) {
12213 MolActionAlertRubyIsRunning();
12215 return (RubyValue)Qnil;
12218 args[0] = (void *)script;
12219 args[1] = (void *)mol;
12220 args[2] = (void *)fname;
12221 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12222 /* save_ruby_sourcefile = ruby_sourcefile;
12223 save_ruby_sourceline = ruby_sourceline; */
12224 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12225 if (*status != 0) {
12226 /* Is this 'exit' exception? */
12227 VALUE last_exception = rb_gv_get("$!");
12228 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12229 /* Capture exit and return the status value */
12230 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12232 rb_set_errinfo(Qnil);
12235 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12236 /* ruby_sourcefile = save_ruby_sourcefile;
12237 ruby_sourceline = save_ruby_sourceline; */
12244 Ruby_inspectValue(RubyValue value)
12247 static char buf[256];
12248 VALUE val = (VALUE)value;
12250 val = rb_protect(rb_inspect, val, &status);
12253 char *str = StringValuePtr(val);
12254 strncpy(buf, str, sizeof(buf) - 1);
12255 buf[sizeof(buf) - 1] = 0;
12257 snprintf(buf, sizeof(buf), "Error status = %d", status);
12263 Ruby_showValue(RubyValue value, char **outValueString)
12265 VALUE val = (VALUE)value;
12266 if (gMolbyIsCheckingInterrupt) {
12267 MolActionAlertRubyIsRunning();
12274 val = rb_protect(rb_inspect, val, &status);
12278 str = StringValuePtr(val);
12279 if (outValueString != NULL)
12280 *outValueString = strdup(str);
12281 MyAppCallback_showScriptMessage("%s", str);
12283 if (outValueString != NULL)
12284 *outValueString = NULL;
12290 Ruby_showError(int status)
12292 static const int tag_raise = 6;
12293 char *main_message = "Molby script error";
12294 char *msg = NULL, *msg2;
12295 VALUE val, backtrace;
12296 int interrupted = 0;
12297 int exit_status = -1;
12298 if (status == tag_raise) {
12299 VALUE errinfo = rb_errinfo();
12300 VALUE eclass = CLASS_OF(errinfo);
12301 if (eclass == rb_eInterrupt) {
12302 main_message = "Molby script interrupted";
12305 } else if (eclass == rb_eSystemExit) {
12306 main_message = "Molby script exit";
12308 val = rb_eval_string_protect("$!.status", &status);
12310 exit_status = NUM2INT(rb_Integer(val));
12311 asprintf(&msg, "Molby script exit with status %d", exit_status);
12313 asprintf(&msg, "Molby script exit with unknown status");
12318 if (exit_status != 0) {
12319 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12321 val = rb_eval_string_protect("$!.to_s", &status);
12323 msg = RSTRING_PTR(val);
12325 msg = "(message not available)";
12327 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12329 msg2 = strdup(msg);
12331 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12333 if (interrupted == 2) {
12335 if (!gUseGUI && exit_status == 0)
12336 exit(0); // Capture exit(0) here and force exit
12341 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12343 Molby_loadScript(const char *script, int from_file)
12348 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12350 rb_eval_string_protect(script, &status);
12356 Molby_getDescription(char **versionString, char **auxString)
12358 extern const char *gVersionString, *gCopyrightString;
12359 extern int gRevisionNumber;
12360 extern char *gLastBuildString;
12362 char *revisionString;
12363 if (gRevisionNumber > 0) {
12364 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12365 } else revisionString = "";
12367 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12368 #if defined(__WXMSW__)
12369 #if TARGET_ARCH == 64
12377 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12382 "ruby %s, http://www.ruby-lang.org/\n"
12384 "FFTW 3.3.2, http://www.fftw.org/\n"
12385 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12386 " and Massachusetts Institute of Technology",
12387 MyAppCallback_getGUIDescriptionString(),
12388 gRubyVersion, gRubyCopyright);
12392 "ruby %s, http://www.ruby-lang.org/\n"
12394 "FFTW 3.3.2, http://www.fftw.org/\n"
12395 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12396 " and Massachusetts Institute of Technology",
12397 gRubyVersion, gRubyCopyright);
12400 if (revisionString[0] != 0)
12401 free(revisionString);
12402 if (versionString != NULL)
12403 *versionString = s1;
12404 if (auxString != NULL)
12409 Molby_startup(const char *script, const char *dir)
12414 char *respath, *p, *wbuf;
12416 /* Get version/copyright string from Ruby interpreter */
12418 gRubyVersion = strdup(ruby_version);
12419 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12420 " ", /* Indent for displaying in About dialog */
12421 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12424 /* Read build and revision information for Molby */
12427 extern int gRevisionNumber;
12428 extern char *gLastBuildString;
12429 FILE *fp = fopen("../buildInfo.txt", "r");
12430 gLastBuildString = "";
12432 if (fgets(buf, sizeof(buf), fp) != NULL) {
12433 char *p1 = strchr(buf, '\"');
12434 char *p2 = strrchr(buf, '\"');
12435 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12436 memmove(buf, p1 + 1, p2 - p1 - 1);
12437 buf[p2 - p1 - 1] = 0;
12438 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12443 fp = fopen("../revisionInfo.txt", "r");
12444 gRevisionNumber = 0;
12446 if (fgets(buf, sizeof(buf), fp) != NULL) {
12447 gRevisionNumber = strtol(buf, NULL, 0);
12455 Molby_getDescription(&wbuf, &wbuf2);
12456 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12461 /* Read atom display parameters */
12462 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12463 MyAppCallback_setConsoleColor(1);
12464 MyAppCallback_showScriptMessage("%s", wbuf);
12465 MyAppCallback_setConsoleColor(0);
12469 /* Read default parameters */
12470 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12471 if (wbuf != NULL) {
12472 MyAppCallback_setConsoleColor(1);
12473 MyAppCallback_showScriptMessage("%s", wbuf);
12474 MyAppCallback_setConsoleColor(0);
12478 /* Initialize Ruby interpreter */
12481 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12482 it causes rb_bug() (= fatal error) during ruby_init().
12483 As a workaround, these standard streams are reopend as
12485 freopen("NUL", "r", stdin);
12486 freopen("NUL", "w", stdout);
12487 freopen("NUL", "w", stderr);
12493 /* Initialize CP932/Windows-31J encodings */
12494 extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
12495 extern int rb_enc_alias(const char *, const char *);
12497 Init_windows_31j();
12498 Init_trans_japanese_sjis();
12499 rb_enc_alias("CP932", "Windows-31J");
12502 #if defined(__WXMSW__)
12504 /* Set default external encoding */
12505 /* The following snippet is taken from encoding.c */
12506 extern void rb_enc_set_default_external(VALUE encoding);
12507 char cp[sizeof(int) * 8 / 3 + 22];
12510 snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12511 enc = rb_eval_string_protect(cp, &status);
12512 if (status == 0 && !NIL_P(enc)) {
12513 rb_enc_set_default_external(enc);
12518 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12520 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12521 ruby_incpush(libpath);
12525 ruby_script("Molby");
12527 /* Find the resource path (the parent directory of the given directory) */
12528 respath = strdup(dir);
12529 p = strrchr(respath, '/');
12530 if (p == NULL && PATH_SEPARATOR != '/')
12531 p = strrchr(respath, PATH_SEPARATOR);
12534 val = Ruby_NewFileStringValue(respath);
12535 rb_define_global_const("MolbyResourcePath", val);
12538 /* Define Molby classes */
12541 RubyDialogInitClass();
12543 rb_define_const(rb_mMolby, "ResourcePath", val);
12544 val = Ruby_NewFileStringValue(dir);
12545 rb_define_const(rb_mMolby, "ScriptPath", val);
12546 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12547 val = Ruby_NewFileStringValue(p);
12548 rb_define_const(rb_mMolby, "MbsfPath", val);
12551 p = MyAppCallback_getHomeDir();
12552 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12553 rb_define_const(rb_mMolby, "HomeDirectory", val);
12555 p = MyAppCallback_getDocumentHomeDir();
12556 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12557 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12561 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12563 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12566 /* Create objects for stdout and stderr */
12567 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12568 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12569 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12570 rb_gv_set("$stdout", val);
12571 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12572 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12573 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12574 rb_gv_set("$stderr", val);
12576 /* Create objects for stdin */
12577 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12578 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12579 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12580 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12581 rb_gv_set("$stdin", val);
12584 /* Global variable to hold error information */
12585 rb_define_variable("$backtrace", &gMolbyBacktrace);
12586 rb_define_variable("$error_history", &gMolbyErrorHistory);
12587 gMolbyErrorHistory = rb_ary_new();
12589 /* Global variables for script menus */
12590 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12591 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12592 gScriptMenuCommands = rb_ary_new();
12593 gScriptMenuEnablers = rb_ary_new();
12596 /* Register interrupt check code */
12597 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12598 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12599 s_SetIntervalTimer(0, 50);
12602 /* Read the startup script */
12603 if (script != NULL && script[0] != 0) {
12604 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12606 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12609 Ruby_showError(status);
12611 MyAppCallback_showScriptMessage("Done.\n");
12616 Molby_buildARGV(int argc, const char **argv)
12619 rb_ary_clear(rb_argv);
12620 for (i = 0; i < argc; i++) {
12621 VALUE arg = rb_tainted_str_new2(argv[i]);
12623 rb_ary_push(rb_argv, arg);