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 argv0 = argv[0]; /* Keep as a local variable to avoid GC */
5410 if (argc == 0 || (argstr = StringValuePtr(argv0)) == NULL)
5411 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5412 if (argstr[0] == ':') {
5413 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5414 methname = ALLOC_N(char, lslen + strlen(argstr));
5415 strcpy(methname, ls);
5416 strcat(methname, argstr + 1);
5418 for (i = lslen; methname[i] != 0; i++)
5419 methname[i] = tolower(methname[i]);
5420 mid = rb_intern(methname);
5424 rval = rb_funcall2(self, mid, argc, argv);
5430 /* Guess file type from extension */
5431 p = strrchr(argstr, '.');
5435 for (methname = p; *methname != 0; methname++) {
5436 if (!isalpha(*methname))
5439 if (*methname == 0) {
5440 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5441 if (methname == NULL)
5442 rb_raise(rb_eMolbyError, "Low memory");
5443 strcpy(methname, ls);
5444 strcat(methname, p);
5445 for (i = lslen; methname[i] != 0; i++)
5446 methname[i] = tolower(methname[i]);
5447 mid = rb_intern(methname);
5450 if (rb_respond_to(self, mid)) {
5451 /* Load: try to call the load procedure only if it is available */
5452 rval = rb_funcall2(self, mid, argc, argv);
5457 /* Save: call the save procedure, and if not found then call 'method_missing' */
5458 rval = rb_funcall2(self, mid, argc, argv);
5465 rval = rb_str_to_str(argv0);
5466 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5467 s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
5468 return Qnil; /* Does not reach here */
5472 /* Register the path */
5475 Data_Get_Struct(self, Molecule, mol);
5476 MoleculeSetPath(mol, StringValuePtr(argv0));
5478 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5479 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5480 if (ap->occupancy != 0.0)
5483 if (i == mol->natoms) {
5484 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5485 ap->occupancy = 1.0;
5494 * molload(file, *args) -> bool
5496 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5497 * file type given by the extension). If this method fails, then all defined (public)
5498 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5501 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5503 return s_Molecule_LoadSave(argc, argv, self, 1);
5508 * molsave(file, *args) -> bool
5510 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5511 * (XXX is the file type given by the extension).
5514 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5516 return s_Molecule_LoadSave(argc, argv, self, 0);
5522 * open(file) -> Molecule
5524 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5527 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5535 rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
5538 rb_scan_args(argc, argv, "01", &fname);
5542 p = FileStringValuePtr(fname);
5543 iflag = Ruby_SetInterruptFlag(Qfalse);
5544 mp = MoleculeCallback_openNewMolecule(p);
5545 Ruby_SetInterruptFlag(iflag);
5548 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5550 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5552 return ValueFromMolecule(mp);
5558 * new(file, *args) -> Molecule
5560 * Create a new molecule and call "load" method with the same arguments.
5563 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5566 return s_Molecule_Load(argc, argv, self);
5567 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5572 * error_message -> String
5574 * Get the error_message from the last load/save method. If no error, returns nil.
5577 s_Molecule_ErrorMessage(VALUE klass)
5579 if (gLoadSaveErrorMessage == NULL)
5581 else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
5586 * set_error_message(String)
5587 * Molecule.error_message = String
5589 * Set the error_message for the present load/save method.
5592 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
5594 if (gLoadSaveErrorMessage != NULL) {
5595 free(gLoadSaveErrorMessage);
5596 gLoadSaveErrorMessage = NULL;
5599 sval = rb_str_to_str(sval);
5600 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
5607 * set_molecule(Molecule)
5609 * Duplicate the given molecule and set to self. The present molecule must be empty.
5610 * This method is exclusively used for associating a new document with an existing molecule.
5613 s_Molecule_SetMolecule(VALUE self, VALUE mval)
5615 Molecule *mp1, *mp2;
5616 Data_Get_Struct(self, Molecule, mp1);
5617 mp2 = MoleculeFromValue(mval);
5618 MoleculeInitWithMolecule(mp1, mp2);
5619 MoleculeCallback_notifyModification(mp1, 1);
5623 #pragma mark ------ Name attributes ------
5629 * Returns the display name of the molecule. If the molecule has no associated
5630 * document, then returns nil.
5633 s_Molecule_Name(VALUE self)
5637 Data_Get_Struct(self, Molecule, mol);
5638 MoleculeCallback_displayName(mol, buf, sizeof buf);
5642 return Ruby_NewEncodedStringValue2(buf);
5647 * set_name(string) -> self
5649 * Set the name of an untitled molecule. If the molecule is not associated with window
5650 * or it already has an associated file, then exception is thrown.
5653 s_Molecule_SetName(VALUE self, VALUE nval)
5656 Data_Get_Struct(self, Molecule, mol);
5657 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5658 rb_raise(rb_eMolbyError, "Cannot change the window title");
5667 * Returns the full path name of the molecule, if it is associated with a file.
5668 * If the molecule has no associated file, then returns nil.
5671 s_Molecule_Path(VALUE self)
5675 Data_Get_Struct(self, Molecule, mol);
5676 MoleculeCallback_pathName(mol, buf, sizeof buf);
5680 return Ruby_NewFileStringValue(buf);
5687 * Returns the full path name of the directory in which the file associated with the
5688 * molecule is located. If the molecule has no associated file, then returns nil.
5691 s_Molecule_Dir(VALUE self)
5695 Data_Get_Struct(self, Molecule, mol);
5696 MoleculeCallback_pathName(mol, buf, sizeof buf);
5698 translate_char(buf, '\\', '/');
5703 p = strrchr(buf, '/');
5706 return Ruby_NewEncodedStringValue2(buf);
5714 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5715 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5716 * the Molecule structure) is returned.
5719 s_Molecule_Inspect(VALUE self)
5723 Data_Get_Struct(self, Molecule, mol);
5724 MoleculeCallback_displayName(mol, buf, sizeof buf);
5726 /* No associated document */
5727 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5728 return Ruby_NewEncodedStringValue2(buf);
5730 /* Check whether the document name is duplicate */
5734 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5735 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5736 if (strcmp(buf, buf2) == 0) {
5743 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5745 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5747 return Ruby_NewEncodedStringValue2(buf2);
5751 #pragma mark ------ MolEnumerables ------
5754 s_Molecule_MolEnumerable(VALUE self, int kind)
5757 MolEnumerable *mseq;
5758 Data_Get_Struct(self, Molecule, mol);
5759 mseq = MolEnumerableNew(mol, kind);
5760 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5765 * atoms -> MolEnumerable
5767 * Returns a MolEnumerable object representing the array of atoms.
5770 s_Molecule_Atoms(VALUE self)
5772 return s_Molecule_MolEnumerable(self, kAtomKind);
5777 * bonds -> MolEnumerable
5779 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5780 * by an array of two atom indices.
5783 s_Molecule_Bonds(VALUE self)
5785 return s_Molecule_MolEnumerable(self, kBondKind);
5790 * angles -> MolEnumerable
5792 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5793 * by an array of three atom indices.
5796 s_Molecule_Angles(VALUE self)
5798 return s_Molecule_MolEnumerable(self, kAngleKind);
5803 * dihedrals -> MolEnumerable
5805 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5806 * by an array of four atom indices.
5809 s_Molecule_Dihedrals(VALUE self)
5811 return s_Molecule_MolEnumerable(self, kDihedralKind);
5816 * impropers -> MolEnumerable
5818 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5819 * by an array of four atom indices.
5822 s_Molecule_Impropers(VALUE self)
5824 return s_Molecule_MolEnumerable(self, kImproperKind);
5829 * residues -> MolEnumerable
5831 * Returns a MolEnumerable object representing the array of residue names.
5834 s_Molecule_Residues(VALUE self)
5836 return s_Molecule_MolEnumerable(self, kResidueKind);
5843 * Returns the number of atoms.
5846 s_Molecule_Natoms(VALUE self)
5849 Data_Get_Struct(self, Molecule, mol);
5850 return INT2NUM(mol->natoms);
5857 * Returns the number of bonds.
5860 s_Molecule_Nbonds(VALUE self)
5863 Data_Get_Struct(self, Molecule, mol);
5864 return INT2NUM(mol->nbonds);
5869 * nangles -> Integer
5871 * Returns the number of angles.
5874 s_Molecule_Nangles(VALUE self)
5877 Data_Get_Struct(self, Molecule, mol);
5878 return INT2NUM(mol->nangles);
5883 * ndihedrals -> Integer
5885 * Returns the number of dihedrals.
5888 s_Molecule_Ndihedrals(VALUE self)
5891 Data_Get_Struct(self, Molecule, mol);
5892 return INT2NUM(mol->ndihedrals);
5897 * nimpropers -> Integer
5899 * Returns the number of impropers.
5902 s_Molecule_Nimpropers(VALUE self)
5905 Data_Get_Struct(self, Molecule, mol);
5906 return INT2NUM(mol->nimpropers);
5911 * nresidues -> Integer
5913 * Returns the number of residues.
5916 s_Molecule_Nresidues(VALUE self)
5919 Data_Get_Struct(self, Molecule, mol);
5920 return INT2NUM(mol->nresidues);
5925 * nresidues = Integer
5927 * Change the number of residues.
5930 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5933 int ival = NUM2INT(val);
5934 Data_Get_Struct(self, Molecule, mol);
5935 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5936 if (ival != mol->nresidues)
5937 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5943 * max_residue_number(atom_group = nil) -> Integer
5945 * Returns the maximum residue number actually used. If an atom group is given, only
5946 * these atoms are examined. If no atom is present, nil is returned.
5949 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5955 Data_Get_Struct(self, Molecule, mol);
5956 rb_scan_args(argc, argv, "01", &gval);
5957 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5958 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5959 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5964 * min_residue_number(atom_group = nil) -> Integer
5966 * Returns the minimum residue number actually used. If an atom group is given, only
5967 * these atoms are examined. If no atom is present, nil is returned.
5970 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5976 Data_Get_Struct(self, Molecule, mol);
5977 rb_scan_args(argc, argv, "01", &gval);
5978 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5979 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5980 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5985 * each_atom(atom_group = nil) {|aref| ...}
5987 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5988 * group is given, only these atoms are processed.
5989 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5990 * is self (a Molecule object).
5993 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
6001 Data_Get_Struct(self, Molecule, mol);
6002 rb_scan_args(argc, argv, "01", &gval);
6003 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
6004 arval = ValueFromMoleculeAndIndex(mol, 0);
6005 Data_Get_Struct(arval, AtomRef, aref);
6006 for (i = 0; i < mol->natoms; i++) {
6008 if (ig == NULL || IntGroupLookup(ig, i, NULL))
6012 IntGroupRelease(ig);
6016 #pragma mark ------ Atom Group ------
6019 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6021 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6022 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6023 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6024 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6031 * atom_group {|aref| ...}
6032 * atom_group(arg1, arg2, ...)
6033 * atom_group(arg1, arg2, ...) {|aref| ...}
6035 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6036 * If arguments are given, then the atoms reprensented by the arguments are added to the
6037 * group. For a conversion of a string to an atom index, see the description
6038 * of Molecule#atom_index.
6039 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6040 * representing each atom, and the atoms are removed from the result if the block returns false.
6044 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6046 IntGroup *ig1, *ig2;
6048 Int i, startPt, interval;
6049 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6050 Data_Get_Struct(retval, IntGroup, ig1);
6051 Data_Get_Struct(self, Molecule, mol);
6053 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6056 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6057 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6058 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6059 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6060 ig2 = IntGroupFromValue(*argv);
6061 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6062 interval = IntGroupGetInterval(ig2, i);
6063 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6065 IntGroupRelease(ig2);
6066 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6068 values[0] = (VALUE)mol;
6069 values[1] = (VALUE)ig1;
6070 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6072 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6077 if (rb_block_given_p()) {
6078 /* Evaluate the given block with an AtomRef as the argument, and delete
6079 the index if the block returns false */
6080 AtomRef *aref = AtomRefNew(mol, 0);
6081 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6082 ig2 = IntGroupNew();
6083 IntGroupCopy(ig2, ig1);
6084 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6086 if (startPt >= mol->natoms)
6088 aref->idx = startPt;
6089 resval = rb_yield(arval);
6091 IntGroupRemove(ig1, startPt, 1);
6093 IntGroupRelease(ig2);
6096 /* Remove points that are out of bounds */
6097 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6104 * selection -> IntGroup
6106 * Returns the current selection.
6109 s_Molecule_Selection(VALUE self)
6114 Data_Get_Struct(self, Molecule, mol);
6115 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6116 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6117 val = ValueFromIntGroup(ig);
6118 IntGroupRelease(ig);
6120 val = IntGroup_Alloc(rb_cIntGroup);
6126 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6130 Data_Get_Struct(self, Molecule, mol);
6134 ig = s_Molecule_AtomGroupFromValue(self, val);
6136 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6138 MoleculeSetSelection(mol, ig);
6140 IntGroupRelease(ig);
6146 * selection = IntGroup
6148 * Set the current selection. The right-hand operand may be nil.
6149 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6152 s_Molecule_SetSelection(VALUE self, VALUE val)
6154 return s_Molecule_SetSelectionSub(self, val, 0);
6159 * set_undoable_selection(IntGroup)
6161 * Set the current selection with undo registration. The right-hand operand may be nil.
6162 * This operation is undoable.
6165 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6167 return s_Molecule_SetSelectionSub(self, val, 1);
6170 #pragma mark ------ Editing ------
6174 * extract(group, dummy_flag = nil) -> Molecule
6176 * Extract the atoms given by group and return as a new molecule object.
6177 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6178 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6179 * names beginning with an underscore) and included in the new molecule object.
6182 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6184 Molecule *mol1, *mol2;
6186 VALUE group, dummy_flag, retval;
6187 Data_Get_Struct(self, Molecule, mol1);
6188 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6189 ig = s_Molecule_AtomGroupFromValue(self, group);
6190 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6193 retval = ValueFromMolecule(mol2);
6195 IntGroupRelease(ig);
6201 * add(molecule2) -> self
6203 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6205 This operation is undoable.
6208 s_Molecule_Add(VALUE self, VALUE val)
6210 Molecule *mol1, *mol2;
6211 Data_Get_Struct(self, Molecule, mol1);
6212 mol2 = MoleculeFromValue(val);
6213 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6219 * remove(group) -> Molecule
6221 * The atoms designated by the given group are removed from the molecule.
6222 * This operation is undoable.
6225 s_Molecule_Remove(VALUE self, VALUE group)
6230 IntGroupIterator iter;
6232 ig = s_Molecule_AtomGroupFromValue(self, group);
6233 /* Data_Get_Struct(self, Molecule, mol1);
6234 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6235 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6236 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6237 Data_Get_Struct(group, IntGroup, ig); */
6238 Data_Get_Struct(self, Molecule, mol1);
6240 /* Remove the bonds between the two fragments */
6241 /* (This is necessary for undo to work correctly) */
6242 IntGroupIteratorInit(ig, &iter);
6244 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6245 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6247 cp = AtomConnectData(&ap->connect);
6248 for (j = 0; j < ap->connect.count; j++) {
6250 if (!IntGroupLookup(ig, n, NULL)) {
6251 /* bond i-n, i is in ig and n is not */
6252 int k = MoleculeLookupBond(mol1, i, n);
6256 IntGroupAdd(bg, k, 1);
6261 IntGroupIteratorRelease(&iter);
6264 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6265 IntGroupRelease(bg);
6268 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6275 * create_atom(name, pos = -1) -> AtomRef
6277 * Create a new atom with the specified name (may contain residue
6278 * information) and position (if position is out of range, the atom is appended at
6279 * the end). Returns the reference to the new atom.
6280 * This operation is undoable.
6283 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6290 char *p, resName[6], atomName[6];
6292 Data_Get_Struct(self, Molecule, mol);
6293 rb_scan_args(argc, argv, "02", &name, &ival);
6295 pos = NUM2INT(rb_Integer(ival));
6298 p = StringValuePtr(name);
6300 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6301 if (atomName[0] == 0)
6302 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6305 if (p == NULL || p[0] == 0) {
6306 memset(atomName, 0, 4);
6309 memset(&arec, 0, sizeof(arec));
6310 strncpy(arec.aname, atomName, 4);
6312 strncpy(arec.resName, resName, 4);
6313 arec.resSeq = resSeq;
6315 arec.occupancy = 1.0;
6316 // i = MoleculeCreateAnAtom(mol, &arec);
6317 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6319 aref = AtomRefNew(mol, pos);
6320 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6325 * duplicate_atom(atomref, pos = -1) -> AtomRef
6327 * Create a new atom with the same attributes (but no bonding information)
6328 * with the specified atom. Returns the reference to the new atom.
6329 * This operation is undoable.
6332 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6338 VALUE retval, aval, ival;
6340 Data_Get_Struct(self, Molecule, mol);
6341 rb_scan_args(argc, argv, "11", &aval, &ival);
6342 if (FIXNUM_P(aval)) {
6343 int idx = NUM2INT(aval);
6344 if (idx < 0 || idx >= mol->natoms)
6345 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6346 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6348 apsrc = s_AtomFromValue(aval);
6351 rb_raise(rb_eMolbyError, "bad atom specification");
6353 pos = NUM2INT(rb_Integer(ival));
6355 AtomDuplicate(&arec, apsrc);
6356 arec.connect.count = 0;
6357 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6360 aref = AtomRefNew(mol, pos);
6361 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6369 * create_bond(n1, n2, ...) -> Integer
6371 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6372 * do nothing for that pair. Returns the number of bonds actually created.
6373 * This operation is undoable.
6376 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6379 Int i, j, k, *ip, old_nbonds;
6381 rb_raise(rb_eMolbyError, "missing arguments");
6383 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6384 Data_Get_Struct(self, Molecule, mol);
6385 ip = ALLOC_N(Int, argc + 1);
6386 for (i = j = 0; i < argc; i++, j++) {
6387 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6389 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6390 j -= 2; /* This bond is already present: skip it */
6392 for (k = 0; k < j - 1; k += 2) {
6393 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6394 j -= 2; /* The same entry is already in the argument */
6401 old_nbonds = mol->nbonds;
6403 ip[j] = kInvalidIndex;
6404 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6408 rb_raise(rb_eMolbyError, "atom index out of range");
6410 rb_raise(rb_eMolbyError, "too many bonds");
6412 rb_raise(rb_eMolbyError, "duplicate bonds");
6414 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6416 rb_raise(rb_eMolbyError, "error in creating bonds");
6417 return INT2NUM(mol->nbonds - old_nbonds);
6422 * molecule.remove_bonds(n1, n2, ...) -> Integer
6424 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6425 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6426 * This operation is undoable.
6429 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6435 rb_raise(rb_eMolbyError, "missing arguments");
6437 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6438 Data_Get_Struct(self, Molecule, mol);
6440 for (i = j = 0; i < argc; i++, j = 1 - j) {
6441 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6443 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6447 IntGroupAdd(bg, k, 1);
6452 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6453 i = IntGroupGetCount(bg);
6454 IntGroupRelease(bg);
6461 * assign_bond_order(idx, d1)
6462 * assign_bond_orders(group, [d1, d2, ...])
6464 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6465 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6466 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6467 * (This may change in the future)
6468 * This operation is undoable.
6471 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6475 Data_Get_Struct(self, Molecule, mol);
6476 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6477 /* The first form */
6478 Int idx = NUM2INT(rb_Integer(idxval));
6479 Double d1 = NUM2DBL(rb_Float(dval));
6480 if (idx < 0 || idx >= mol->nbonds)
6481 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6482 ig = IntGroupNewWithPoints(idx, 1, -1);
6483 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6484 IntGroupRelease(ig);
6488 ig = IntGroupFromValue(idxval);
6489 n = IntGroupGetCount(ig);
6491 rb_raise(rb_eMolbyError, "the bond index is empty");
6492 dval = rb_ary_to_ary(dval);
6493 dp = (Double *)calloc(sizeof(Double), n);
6494 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6495 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6497 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6499 IntGroupRelease(ig);
6506 * get_bond_order(idx) -> Float
6507 * get_bond_orders(group) -> Array
6509 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6510 * In the second form, the bond orders at the indices in the group are returned as an array.
6511 * If no bond order information have been assigned, returns nil (the first form)
6512 * or an empty array (the second form).
6515 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6521 Int i, n, numericArg;
6522 Data_Get_Struct(self, Molecule, mol);
6523 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6524 /* The first form */
6525 Int idx = NUM2INT(rb_Integer(idxval));
6526 if (idx < 0 || idx >= mol->nbonds)
6527 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6528 if (mol->bondOrders == NULL)
6530 ig = IntGroupNewWithPoints(idx, 1, -1);
6534 if (mol->bondOrders == NULL)
6535 return rb_ary_new();
6536 ig = IntGroupFromValue(idxval);
6537 n = IntGroupGetCount(ig);
6539 rb_raise(rb_eMolbyError, "the bond index is empty");
6542 dp = (Double *)calloc(sizeof(Double), n);
6543 MoleculeGetBondOrders(mol, dp, ig);
6545 retval = rb_float_new(dp[0]);
6547 retval = rb_ary_new();
6548 for (i = 0; i < n; i++)
6549 rb_ary_push(retval, rb_float_new(dp[i]));
6552 IntGroupRelease(ig);
6558 * bond_exist?(idx1, idx2) -> bool
6560 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6561 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6564 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6570 Data_Get_Struct(self, Molecule, mol);
6571 idx1 = NUM2INT(rb_Integer(ival1));
6572 idx2 = NUM2INT(rb_Integer(ival2));
6573 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6574 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6575 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6576 cp = AtomConnectData(&ap->connect);
6577 for (i = 0; i < ap->connect.count; i++) {
6586 * add_angle(n1, n2, n3) -> Molecule
6588 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6589 * when a bond is created, so it is rarely necessary to use this method explicitly.
6590 * This operation is undoable.
6593 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6597 Data_Get_Struct(self, Molecule, mol);
6598 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6599 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6600 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6601 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6602 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6603 n[3] = kInvalidIndex;
6604 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6610 * remove_angle(n1, n2, n3) -> Molecule
6612 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6613 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6614 * This operation is undoable.
6617 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6622 Data_Get_Struct(self, Molecule, mol);
6623 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6624 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6625 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6626 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6627 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6628 ig = IntGroupNewWithPoints(n[3], 1, -1);
6629 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6630 IntGroupRelease(ig);
6636 * add_dihedral(n1, n2, n3, n4) -> Molecule
6638 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6639 * when a bond is created, so it is rarely necessary to use this method explicitly.
6640 * This operation is undoable.
6643 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6647 Data_Get_Struct(self, Molecule, mol);
6648 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6649 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6650 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6651 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6652 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6653 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6654 n[4] = kInvalidIndex;
6655 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6661 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6663 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6664 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6665 * This operation is undoable.
6668 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6673 Data_Get_Struct(self, Molecule, mol);
6674 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6675 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6676 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6677 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6678 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6679 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6680 ig = IntGroupNewWithPoints(n[4], 1, -1);
6681 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6682 IntGroupRelease(ig);
6688 * add_improper(n1, n2, n3, n4) -> Molecule
6690 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6691 * not automatically added when a new bond is created, so this method is more useful than
6692 * the angle/dihedral counterpart.
6693 * This operation is undoable.
6696 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6700 Data_Get_Struct(self, Molecule, mol);
6701 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6702 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6703 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6704 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6705 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6706 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6707 n[4] = kInvalidIndex;
6708 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6714 * remove_improper(n1, n2, n3, n4) -> Molecule
6715 * remove_improper(intgroup) -> Molecule
6717 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6718 * Returns self. Unlike angles and dihedrals, impropers are
6719 * not automatically added when a new bond is created, so this method is more useful than
6720 * the angle/dihedral counterpart.
6721 * This operation is undoable.
6724 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6727 VALUE v1, v2, v3, v4;
6730 Data_Get_Struct(self, Molecule, mol);
6732 ig = IntGroupFromValue(argv[0]);
6734 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6735 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6736 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6737 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6738 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6739 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6740 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6741 ig = IntGroupNewWithPoints(n[4], 1, -1);
6743 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6744 IntGroupRelease(ig);
6750 * assign_residue(group, res) -> Molecule
6752 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6753 * or "resname.resno". When the residue number is not specified, the residue number of
6754 * the first atom in the group is used.
6755 * This operation is undoable.
6758 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6762 char *p, *pp, buf[16];
6765 Data_Get_Struct(self, Molecule, mol);
6767 /* Parse the argument res */
6768 if (FIXNUM_P(res)) {
6769 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6770 resid = NUM2INT(res);
6773 p = StringValuePtr(res);
6774 pp = strchr(p, '.');
6776 resid = atoi(pp + 1);
6782 if (n > sizeof buf - 1)
6787 ig = s_Molecule_AtomGroupFromValue(self, range);
6788 if (ig == NULL || IntGroupGetCount(ig) == 0)
6792 /* Use the residue number of the first specified atom */
6793 n = IntGroupGetNthPoint(ig, 0);
6794 if (n >= mol->natoms)
6795 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6796 ap = ATOM_AT_INDEX(mol->atoms, n);
6799 /* Change the residue number */
6800 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6801 /* Change the residue name if necessary */
6805 seqs[1] = kInvalidIndex; */
6806 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6808 IntGroupRelease(ig);
6814 * offset_residue(group, offset) -> Molecule
6816 * Offset the residue number of the specified atoms. If any of the residue number gets
6817 * negative, then exception is thrown.
6818 * This operation is undoable.
6821 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6826 Data_Get_Struct(self, Molecule, mol);
6827 ig = s_Molecule_AtomGroupFromValue(self, range);
6828 ofs = NUM2INT(offset);
6829 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6831 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6832 IntGroupRelease(ig);
6838 * renumber_atoms(array) -> IntGroup
6840 * Change the order of atoms so that the atoms specified in the array argument appear
6841 * in this order from the top of the molecule. The atoms that are not included in array
6842 * are placed after these atoms, and these atoms are returned as an intGroup.
6843 * This operation is undoable.
6846 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6852 VALUE *valp, retval;
6853 Data_Get_Struct(self, Molecule, mol);
6854 if (TYPE(array) != T_ARRAY)
6855 array = rb_funcall(array, rb_intern("to_a"), 0);
6856 n = RARRAY_LEN(array);
6857 valp = RARRAY_PTR(array);
6858 new2old = ALLOC_N(Int, n + 1);
6859 for (i = 0; i < n; i++)
6860 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6861 new2old[i] = kInvalidIndex;
6862 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6864 rb_raise(rb_eMolbyError, "Atom index out of range");
6866 rb_raise(rb_eMolbyError, "Duplicate entry");
6868 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6869 retval = IntGroup_Alloc(rb_cIntGroup);
6870 Data_Get_Struct(retval, IntGroup, ig);
6871 if (mol->natoms > n)
6872 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6879 * set_atom_attr(index, key, value)
6881 * Set the atom attribute for the specified atom.
6882 * This operation is undoable.
6885 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6889 Data_Get_Struct(self, Molecule, mol);
6890 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6891 oldval = s_AtomRef_GetAttr(aref, key);
6894 s_AtomRef_SetAttr(aref, key, val);
6900 * get_atom_attr(index, key)
6902 * Get the atom attribute for the specified atom.
6905 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6907 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6910 #pragma mark ------ Undo Support ------
6914 * register_undo(script, *args)
6916 * Register an undo operation with the current molecule.
6919 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6924 Data_Get_Struct(self, Molecule, mol);
6925 rb_scan_args(argc, argv, "1*", &script, &args);
6926 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6927 MolActionCallback_registerUndo(mol, act);
6933 * undo_enabled? -> bool
6935 * Returns true if undo is enabled for this molecule; otherwise no.
6938 s_Molecule_UndoEnabled(VALUE self)
6941 Data_Get_Struct(self, Molecule, mol);
6942 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6949 * undo_enabled = bool
6951 * Enable or disable undo.
6954 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6957 Data_Get_Struct(self, Molecule, mol);
6958 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6962 #pragma mark ------ Measure ------
6965 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
6967 switch (MoleculeCenterOfMass(mol, outv, ig)) {
6968 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
6969 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
6971 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
6977 * center_of_mass(group = nil) -> Vector3D
6979 * Calculate the center of mass for the given set of atoms. The argument
6980 * group is null, then all atoms are considered.
6983 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
6989 Data_Get_Struct(self, Molecule, mol);
6990 rb_scan_args(argc, argv, "01", &group);
6991 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
6992 s_Molecule_DoCenterOfMass(mol, &v, ig);
6994 IntGroupRelease(ig);
6995 return ValueFromVector(&v);
7000 * centralize(group = nil) -> self
7002 * Translate the molecule so that the center of mass of the given group is located
7003 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7006 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7012 Data_Get_Struct(self, Molecule, mol);
7013 rb_scan_args(argc, argv, "01", &group);
7014 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7015 s_Molecule_DoCenterOfMass(mol, &v, ig);
7017 IntGroupRelease(ig);
7021 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7027 * bounds(group = nil) -> [min, max]
7029 * Calculate the boundary. The return value is an array of two Vector3D objects.
7032 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7040 Data_Get_Struct(self, Molecule, mol);
7041 rb_scan_args(argc, argv, "01", &group);
7042 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7043 if (ig != NULL && IntGroupGetCount(ig) == 0)
7044 rb_raise(rb_eMolbyError, "atom group is empty");
7045 vmin.x = vmin.y = vmin.z = 1e30;
7046 vmax.x = vmax.y = vmax.z = -1e30;
7047 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7049 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7065 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7068 /* Get atom position or a vector */
7070 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7072 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7073 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7074 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7076 VectorFromValue(val, vp);
7082 * measure_bond(n1, n2) -> Float
7084 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7085 * or Vector3D values.
7086 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7089 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7093 Data_Get_Struct(self, Molecule, mol);
7094 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7095 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7096 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7101 * measure_angle(n1, n2, n3) -> Float
7103 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7104 * or Vector3D values. The return value is in degree.
7105 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7108 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7113 Data_Get_Struct(self, Molecule, mol);
7114 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7115 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7116 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7117 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7119 return Qnil; /* Cannot define */
7120 else return rb_float_new(d);
7125 * measure_dihedral(n1, n2, n3, n4) -> Float
7127 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7128 * or Vector3D values. The return value is in degree.
7129 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7132 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7135 Vector v1, v2, v3, v4;
7137 Data_Get_Struct(self, Molecule, mol);
7138 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7139 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7140 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7141 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7142 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7144 return Qnil; /* Cannot define */
7145 else return rb_float_new(d);
7150 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7152 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7153 * first and second atom in the pair should belong to group1 and group2, respectively.
7154 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7157 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7160 VALUE limval, gval1, gval2, rval, igval;
7161 IntGroup *ig1, *ig2;
7162 IntGroupIterator iter1, iter2;
7168 MDExclusion *exinfo;
7171 Data_Get_Struct(self, Molecule, mol);
7172 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7173 lim = NUM2DBL(rb_Float(limval));
7175 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7177 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7179 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7181 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7183 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7185 if (!RTEST(igval)) {
7186 /* Use the exclusion table in MDArena */
7187 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7188 VALUE mval = ValueFromMolecule(mol);
7189 s_RebuildMDParameterIfNecessary(mval, Qnil);
7191 exinfo = mol->arena->exinfo; /* May be NULL */
7192 exlist = mol->arena->exlist;
7197 IntGroupIteratorInit(ig1, &iter1);
7198 IntGroupIteratorInit(ig2, &iter2);
7201 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7203 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7205 if (exinfo != NULL) {
7206 exn1 = exinfo[n[0]].index1;
7207 exn2 = exinfo[n[0] + 1].index1;
7208 } else exn1 = exn2 = -1;
7209 IntGroupIteratorReset(&iter2);
7210 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7211 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7213 continue; /* Same atom */
7214 if (exinfo != NULL) {
7215 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7216 for (i = exn1; i < exn2; i++) {
7217 if (exlist[i] == n[1])
7221 continue; /* Should be excluded */
7223 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7224 /* Is this pair already registered? */
7226 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7227 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7231 /* Not registered yet */
7232 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7237 IntGroupIteratorRelease(&iter2);
7238 IntGroupIteratorRelease(&iter1);
7239 IntGroupRelease(ig2);
7240 IntGroupRelease(ig1);
7241 rval = rb_ary_new2(npairs);
7242 if (pairs != NULL) {
7243 for (i = 0; i < npairs; i++) {
7244 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7253 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7255 * Find atoms that are within the threshold distance from the given atom.
7256 * (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.)
7257 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7258 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7259 * If limit is not given, a default value of 1.2 is used.
7260 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7263 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7266 VALUE aval, limval, radval;
7267 double limit, radius;
7268 Int n1, nbonds, *bonds, an;
7270 Data_Get_Struct(self, Molecule, mol);
7271 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7272 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)) {
7273 VectorFromValue(aval, &v);
7275 radius = gElementParameters[6].radius;
7277 radius = NUM2DBL(rb_Float(radval));
7280 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7281 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7282 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7283 if (an >= 0 && an < gCountElementParameters)
7284 radius = gElementParameters[an].radius;
7285 else radius = gElementParameters[6].radius;
7290 limit = NUM2DBL(rb_Float(limval));
7291 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7293 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7294 aval = rb_ary_new();
7296 for (n1 = 0; n1 < nbonds; n1++)
7297 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7305 * guess_bonds(limit = 1.2) -> Integer
7307 * Create bonds between atoms that are within the threshold distance.
7308 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7309 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7310 * If limit is not given, a default value of 1.2 is used.
7311 * The number of the newly created bonds is returned.
7312 * This operation is undoable.
7315 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7321 Data_Get_Struct(self, Molecule, mol);
7322 rb_scan_args(argc, argv, "01", &limval);
7326 limit = NUM2DBL(rb_Float(limval));
7327 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7329 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7332 return INT2NUM(nbonds);
7335 #pragma mark ------ Cell and Symmetry ------
7339 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7341 * Returns the unit cell parameters. If cell is not set, returns nil.
7344 s_Molecule_Cell(VALUE self)
7349 Data_Get_Struct(self, Molecule, mol);
7350 if (mol->cell == NULL)
7352 val = rb_ary_new2(6);
7353 for (i = 0; i < 6; i++)
7354 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
7355 if (mol->cell->has_sigma) {
7356 for (i = 0; i < 6; i++) {
7357 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
7365 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
7366 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
7368 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
7369 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
7370 This operation is undoable.
7371 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
7374 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
7378 int i, convert_coord, n;
7380 Data_Get_Struct(self, Molecule, mol);
7381 rb_scan_args(argc, argv, "11", &val, &cval);
7386 val = rb_ary_to_ary(val);
7387 len = RARRAY_LEN(val);
7390 } else if (len >= 6) {
7392 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
7393 for (i = 0; i < n; i++)
7394 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
7396 convert_coord = (RTEST(cval) ? 1 : 0);
7397 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
7403 * box -> [avec, bvec, cvec, origin, flags]
7405 * Get the unit cell information in the form of a periodic bounding box.
7406 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
7407 * Integers which define whether the system is periodic along the axis.
7408 * If no unit cell is defined, nil is returned.
7411 s_Molecule_Box(VALUE self)
7415 Data_Get_Struct(self, Molecule, mol);
7416 if (mol == NULL || mol->cell == NULL)
7418 v[0] = ValueFromVector(&(mol->cell->axes[0]));
7419 v[1] = ValueFromVector(&(mol->cell->axes[1]));
7420 v[2] = ValueFromVector(&(mol->cell->axes[2]));
7421 v[3] = ValueFromVector(&(mol->cell->origin));
7422 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
7423 val = rb_ary_new4(5, v);
7429 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
7430 * set_box(d, origin = [0, 0, 0])
7433 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
7434 If it is a number, the x/y/z axis vector is multiplied with the given number and used
7436 Flags, if present, is a 3-member array of Integers defining whether the system is
7437 periodic along the axis.
7438 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
7439 In the second form, an isotropic box with cell-length d is set.
7440 In the third form, the existing box is cleared.
7441 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
7444 s_Molecule_SetBox(VALUE self, VALUE aval)
7448 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
7450 Vector origin = {0, 0, 0};
7453 int i, convertCoordinates = 0;
7454 Data_Get_Struct(self, Molecule, mol);
7456 MolActionCreateAndPerform(mol, gMolActionClearBox);
7459 aval = rb_ary_to_ary(aval);
7460 for (i = 0; i < 6; i++) {
7461 if (i < RARRAY_LEN(aval))
7462 v[i] = (RARRAY_PTR(aval))[i];
7466 MolActionCreateAndPerform(mol, gMolActionClearBox);
7469 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
7470 d = NUM2DBL(rb_Float(v[0]));
7471 for (i = 0; i < 3; i++)
7472 VecScale(vv[i], ax[i], d);
7474 VectorFromValue(v[1], &origin);
7475 flags[0] = flags[1] = flags[2] = 1;
7477 for (i = 0; i < 3; i++) {
7480 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
7481 d = NUM2DBL(rb_Float(v[i]));
7482 VecScale(vv[i], ax[i], d);
7484 VectorFromValue(v[i], &vv[i]);
7486 flags[i] = (VecLength2(vv[i]) > 0.0);
7489 VectorFromValue(v[3], &origin);
7491 for (i = 0; i < 3; i++) {
7492 VALUE val = Ruby_ObjectAtIndex(v[4], i);
7493 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
7497 convertCoordinates = 1;
7499 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
7505 * cell_periodicity -> [n1, n2, n3]
7507 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
7511 s_Molecule_CellPeriodicity(VALUE self)
7514 Data_Get_Struct(self, Molecule, mol);
7515 if (mol->cell == NULL)
7517 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
7522 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
7523 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
7525 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
7526 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
7527 * If cell is not defined, exception is raised.
7528 * This operation is undoable.
7531 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
7535 Data_Get_Struct(self, Molecule, mol);
7536 if (mol->cell == NULL)
7537 rb_raise(rb_eMolbyError, "periodic cell is not defined");
7540 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
7541 flag = NUM2INT(rb_Integer(arg));
7545 arg = rb_ary_to_ary(arg);
7547 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
7548 arg0 = RARRAY_PTR(arg)[i];
7549 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
7550 flag |= (1 << (2 - i));
7553 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
7559 * cell_flexibility -> bool
7561 * Returns the unit cell is flexible or not
7564 s_Molecule_CellFlexibility(VALUE self)
7566 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
7569 Data_Get_Struct(self, Molecule, mol);
7570 if (mol->cell == NULL)
7572 if (mol->useFlexibleCell)
7574 else return Qfalse; */
7579 * self.cell_flexibility = bool
7580 * set_cell_flexibility(bool)
7582 * Change the unit cell is flexible or not
7585 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
7587 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
7590 Data_Get_Struct(self, Molecule, mol);
7591 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
7597 * cell_transform -> Transform
7599 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
7600 * If cell is not defined, nil is returned.
7603 s_Molecule_CellTransform(VALUE self)
7606 Data_Get_Struct(self, Molecule, mol);
7607 if (mol == NULL || mol->cell == NULL)
7609 return ValueFromTransform(&(mol->cell->tr));
7614 * symmetry -> Array of Transforms
7615 * symmetries -> Array of Transforms
7617 * Get the currently defined symmetry operations. If no symmetry operation is defined,
7618 * returns an empty array.
7621 s_Molecule_Symmetry(VALUE self)
7626 Data_Get_Struct(self, Molecule, mol);
7627 if (mol->nsyms <= 0)
7628 return rb_ary_new();
7629 val = rb_ary_new2(mol->nsyms);
7630 for (i = 0; i < mol->nsyms; i++) {
7631 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
7638 * nsymmetries -> Integer
7640 * Get the number of currently defined symmetry operations.
7643 s_Molecule_Nsymmetries(VALUE self)
7646 Data_Get_Struct(self, Molecule, mol);
7647 return INT2NUM(mol->nsyms);
7652 * add_symmetry(Transform) -> Integer
7654 * Add a new symmetry operation. If no symmetry operation is defined and the
7655 * given argument is not an identity transform, then also add an identity
7656 * transform at the index 0.
7657 * Returns the total number of symmetries after operation.
7660 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
7664 Data_Get_Struct(self, Molecule, mol);
7665 TransformFromValue(trans, &tr);
7666 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
7667 return INT2NUM(mol->nsyms);
7672 * remove_symmetry(count = nil) -> Integer
7673 * remove_symmetries(count = nil) -> Integer
7675 * Remove the specified number of symmetry operations. The last added ones are removed
7676 * first. If count is nil, then all symmetry operations are removed. Returns the
7677 * number of leftover symmetries.
7680 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
7685 Data_Get_Struct(self, Molecule, mol);
7686 rb_scan_args(argc, argv, "01", &cval);
7690 n = NUM2INT(rb_Integer(cval));
7691 if (n < 0 || n > mol->nsyms)
7692 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
7693 if (n == mol->nsyms)
7696 for (i = 0; i < n; i++)
7697 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
7698 return INT2NUM(mol->nsyms);
7703 * wrap_unit_cell(group) -> Vector3D
7705 * Move the specified group so that the center of mass of the group is within the
7706 * unit cell. The offset vector is returned. If no periodic box is defined,
7707 * exception is raised.
7710 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7715 Data_Get_Struct(self, Molecule, mol);
7716 if (mol->cell == NULL)
7717 rb_raise(rb_eMolbyError, "no unit cell is defined");
7718 ig = s_Molecule_AtomGroupFromValue(self, gval);
7719 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7720 TransformVec(&v, mol->cell->rtr, &cv);
7721 if (mol->cell->flags[0])
7723 if (mol->cell->flags[1])
7725 if (mol->cell->flags[2])
7727 TransformVec(&dv, mol->cell->tr, &v);
7729 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7730 IntGroupRelease(ig);
7731 return ValueFromVector(&dv);
7736 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
7738 * Expand the specified part of the molecule by the given symmetry operation.
7739 * Returns the array of atom indices corresponding to the expanded atoms.
7740 * If allow_overlap is true, then new atoms are created even when the
7741 * coordinates coincide with the some other atom (special position) of the
7742 * same element; otherwise, such atom will not be created and the index of the
7743 * existing atom is given in the returned array.
7746 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7749 VALUE gval, sval, xval, yval, zval, rval, oval;
7751 Int n[4], allow_overlap;
7755 Data_Get_Struct(self, Molecule, mol);
7756 rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
7757 n[0] = NUM2INT(rb_Integer(sval));
7758 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7759 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7760 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7761 allow_overlap = (RTEST(oval) ? 1 : 0);
7762 ig = s_Molecule_AtomGroupFromValue(self, gval);
7763 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
7764 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7765 natoms = mol->natoms;
7767 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
7769 rval = rb_ary_new2(nidx);
7770 while (--nidx >= 0) {
7771 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7773 /* if (natoms == mol->natoms)
7776 rval = IntGroup_Alloc(rb_cIntGroup);
7777 Data_Get_Struct(rval, IntGroup, ig);
7778 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7785 * amend_by_symmetry(group = nil) -> IntGroup
7787 * Expand the specified part of the molecule by the given symmetry operation.
7788 * Returns an IntGroup containing the added atoms.
7791 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7796 Data_Get_Struct(self, Molecule, mol);
7797 rb_scan_args(argc, argv, "01", &gval);
7799 ig = s_Molecule_AtomGroupFromValue(self, gval);
7801 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7802 rval = ValueFromIntGroup(ig2);
7803 IntGroupRelease(ig2);
7807 #pragma mark ------ Transforms ------
7811 * translate(vec, group = nil) -> Molecule
7813 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7814 * This operation is undoable.
7817 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7823 Data_Get_Struct(self, Molecule, mol);
7824 rb_scan_args(argc, argv, "11", &vec, &group);
7825 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7826 VectorFromValue(vec, &v);
7827 // MoleculeTranslate(mol, &v, ig);
7828 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7830 IntGroupRelease(ig);
7836 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7838 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7839 * If group is given, only atoms in the group are moved.
7840 * This operation is undoable.
7843 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7846 volatile VALUE aval, anval, cval, gval;
7851 Data_Get_Struct(self, Molecule, mol);
7852 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7853 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7854 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7855 VectorFromValue(aval, &av);
7857 cv.x = cv.y = cv.z = 0.0;
7859 VectorFromValue(cval, &cv);
7860 if (TransformForRotation(tr, &av, angle, &cv))
7861 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7862 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7864 IntGroupRelease(ig);
7870 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7872 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7873 * axis must not be a zero vector.
7874 * If group is given, only atoms in the group are moved.
7875 * This operation is undoable.
7878 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7881 volatile VALUE aval, cval, gval;
7885 Data_Get_Struct(self, Molecule, mol);
7886 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7887 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7888 VectorFromValue(aval, &av);
7890 cv.x = cv.y = cv.z = 0.0;
7892 VectorFromValue(cval, &cv);
7893 if (TransformForReflection(tr, &av, &cv))
7894 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7895 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7897 IntGroupRelease(ig);
7903 * invert(center = [0,0,0], group = nil) -> Molecule
7905 * Invert the molecule with the given center.
7906 * If group is given, only atoms in the group are moved.
7907 * This operation is undoable.
7910 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7913 volatile VALUE cval, gval;
7917 Data_Get_Struct(self, Molecule, mol);
7918 rb_scan_args(argc, argv, "02", &cval, &gval);
7919 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7921 cv.x = cv.y = cv.z = 0.0;
7923 VectorFromValue(cval, &cv);
7924 TransformForInversion(tr, &cv);
7925 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7927 IntGroupRelease(ig);
7933 * transform(transform, group = nil) -> Molecule
7935 * Transform the molecule by the given Transform object.
7936 * If group is given, only atoms in the group are moved.
7937 * This operation is undoable.
7940 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7946 Data_Get_Struct(self, Molecule, mol);
7947 rb_scan_args(argc, argv, "11", &trans, &group);
7948 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7949 TransformFromValue(trans, &tr);
7950 /* MoleculeTransform(mol, tr, ig); */
7951 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7953 IntGroupRelease(ig);
7959 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7961 * Get the transform corresponding to the symmetry operation. The symop can either be
7962 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7963 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7964 * Otherwise, the returned transform is for fractional coordinates.
7965 * Raises exception when no cell or no transform are defined.
7968 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7974 Data_Get_Struct(self, Molecule, mol);
7975 if (mol->cell == NULL)
7976 rb_raise(rb_eMolbyError, "no unit cell is defined");
7977 if (mol->nsyms == 0)
7978 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7979 rb_scan_args(argc, argv, "11", &sval, &fval);
7980 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7981 symop.sym = NUM2INT(rb_Integer(sval));
7982 symop.dx = symop.dy = symop.dz = 0;
7984 sval = rb_ary_to_ary(sval);
7985 if (RARRAY_LEN(sval) < 4)
7986 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7987 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7988 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7989 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7990 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7992 if (symop.sym >= mol->nsyms)
7993 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7994 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7995 return ValueFromTransform(&tr);
8000 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8002 * Get the symmetry operation corresponding to the given transform.
8003 * If is_cartesian is true, the given transform is for cartesian coordinates.
8004 * Otherwise, the given transform is for fractional coordinates.
8005 * Raises exception when no cell or no transform are defined.
8008 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8015 Data_Get_Struct(self, Molecule, mol);
8016 if (mol->cell == NULL)
8017 rb_raise(rb_eMolbyError, "no unit cell is defined");
8018 if (mol->nsyms == 0)
8019 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8020 rb_scan_args(argc, argv, "11", &tval, &fval);
8021 TransformFromValue(tval, &tr);
8022 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8024 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8026 return Qnil; /* Not found */
8030 #pragma mark ------ Frames ------
8034 * select_frame(index)
8037 * Select the specified frame. If successful, returns true, otherwise returns false.
8040 s_Molecule_SelectFrame(VALUE self, VALUE val)
8043 int ival = NUM2INT(val);
8044 Data_Get_Struct(self, Molecule, mol);
8045 ival = MoleculeSelectFrame(mol, ival, 1);
8055 * Get the current frame.
8058 s_Molecule_Frame(VALUE self)
8061 Data_Get_Struct(self, Molecule, mol);
8062 return INT2NUM(mol->cframe);
8067 * nframes -> Integer
8069 * Get the number of frames.
8072 s_Molecule_Nframes(VALUE self)
8075 Data_Get_Struct(self, Molecule, mol);
8076 return INT2NUM(MoleculeGetNumberOfFrames(mol));
8081 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
8082 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
8084 * Insert new frames at the indices specified by the intGroup. If the first argument is
8085 * an integer, a single new frame is inserted at that index. If the first argument is
8086 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
8087 * should be an array of arrays of Vector3Ds, then those coordinates are set
8088 * to the new frame. Otherwise, the coordinates of current molecule are copied
8090 * Returns an intGroup representing the inserted frames if successful, nil if not.
8093 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
8095 VALUE val, coords, cells;
8098 int count, ival, i, j, len, len_c, len2, nframes;
8101 Data_Get_Struct(self, Molecule, mol);
8102 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
8103 if (coords != Qnil) {
8104 if (TYPE(coords) != T_ARRAY)
8105 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
8106 len = RARRAY_LEN(coords);
8108 if (cells != Qnil) {
8109 if (mol->cell == NULL)
8110 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
8111 if (TYPE(cells) != T_ARRAY)
8112 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
8113 len_c = RARRAY_LEN(cells);
8115 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
8116 nframes = MoleculeGetNumberOfFrames(mol);
8118 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
8119 val = ValueFromIntGroup(ig);
8121 ig = IntGroupFromValue(val);
8123 count = IntGroupGetCount(ig); /* Count is updated here */
8124 vp = ALLOC_N(Vector, mol->natoms * count);
8126 vp2 = ALLOC_N(Vector, 4 * count);
8130 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
8131 ptr = RARRAY_PTR(coords);
8132 for (i = 0; i < count; i++) {
8133 if (TYPE(ptr[i]) != T_ARRAY)
8134 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
8135 len2 = RARRAY_LEN(ptr[i]);
8136 if (len2 < mol->natoms)
8137 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
8138 ptr2 = RARRAY_PTR(ptr[i]);
8139 for (j = 0; j < mol->natoms; j++)
8140 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
8144 for (i = 0; i < count; i++) {
8145 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8146 vp[i * mol->natoms + j] = ap->r;
8152 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
8153 ptr = RARRAY_PTR(cells);
8154 for (i = 0; i < count; i++) {
8155 if (TYPE(ptr[i]) != T_ARRAY)
8156 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
8157 len2 = RARRAY_LEN(ptr[i]);
8159 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
8160 ptr2 = RARRAY_PTR(ptr[i]);
8161 for (j = 0; j < 4; j++)
8162 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
8165 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
8166 IntGroupRelease(ig);
8170 return (ival >= 0 ? val : Qnil);
8175 * create_frame(coordinates = nil) -> Integer
8176 * create_frames(coordinates = nil) -> Integer
8178 * Same as molecule.insert_frames(nil, coordinates).
8181 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
8184 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
8186 return s_Molecule_InsertFrames(3, vals, self);
8191 * remove_frames(IntGroup, wantCoordinates = false)
8193 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
8194 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
8195 * removed frames is returned if operation is successful.
8198 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
8205 Data_Get_Struct(self, Molecule, mol);
8206 rb_scan_args(argc, argv, "11", &val, &flag);
8207 ig = IntGroupFromValue(val);
8208 count = IntGroupGetCount(ig);
8210 /* Create return value before removing frames */
8215 retval = rb_ary_new2(count);
8216 for (i = 0; i < count; i++) {
8217 n = IntGroupGetNthPoint(ig, i);
8218 coords = rb_ary_new2(mol->natoms);
8219 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
8220 if (n < ap->nframes && n != mol->cframe)
8223 rb_ary_push(coords, ValueFromVector(&v));
8225 rb_ary_push(retval, coords);
8227 } else retval = Qtrue;
8228 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
8235 * each_frame {|n| ...}
8237 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
8238 * the frame number. After completion, the original frame number is restored.
8241 s_Molecule_EachFrame(VALUE self)
8243 int i, cframe, nframes;
8245 Data_Get_Struct(self, Molecule, mol);
8246 cframe = mol->cframe;
8247 nframes = MoleculeGetNumberOfFrames(mol);
8249 for (i = 0; i < nframes; i++) {
8250 MoleculeSelectFrame(mol, i, 1);
8251 rb_yield(INT2NUM(i));
8253 MoleculeSelectFrame(mol, cframe, 1);
8260 * get_coord_from_frame(index, group = nil)
8262 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
8263 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
8264 * copied; now they are always copied)
8267 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
8270 VALUE ival, gval, cval;
8271 Int index, i, j, n, nn;
8273 IntGroupIterator iter;
8276 Data_Get_Struct(self, Molecule, mol);
8277 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
8279 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
8280 index = NUM2INT(rb_Integer(ival));
8281 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
8283 rb_raise(rb_eMolbyError, "No frame is present");
8285 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
8288 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8290 ig = s_Molecule_AtomGroupFromValue(self, gval);
8292 n = IntGroupGetCount(ig);
8294 vp = (Vector *)calloc(sizeof(Vector), n);
8295 IntGroupIteratorInit(ig, &iter);
8298 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8299 ap = ATOM_AT_INDEX(mol->atoms, i);
8300 if (index < ap->nframes) {
8301 vp[j] = ap->frames[index];
8309 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
8311 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
8312 vp = mol->frame_cells + index * 4;
8313 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
8315 IntGroupIteratorRelease(&iter);
8317 /* Copy the extra properties */
8318 IntGroupRelease(ig);
8319 for (i = 0; i < mol->nmolprops; i++) {
8320 Double *dp = (Double *)malloc(sizeof(Double));
8322 IntGroupAdd(ig, mol->cframe, 1);
8323 *dp = mol->molprops[i].propvals[index];
8324 MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
8326 IntGroupRelease(ig);
8334 * reorder_frames(old_indices)
8336 * Reorder the frames. The argument is an array of integers that specify the 'old'
8337 * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
8338 * same as the old frames 2/0/1, respectively.
8339 * The argument must have the same number of integers as the number of frames.
8342 s_Molecule_ReorderFrames(VALUE self, VALUE aval)
8345 Int *ip, *ip2, i, n, nframes;
8346 Data_Get_Struct(self, Molecule, mol);
8347 aval = rb_ary_to_ary(aval);
8348 nframes = MoleculeGetNumberOfFrames(mol);
8349 if (RARRAY_LEN(aval) != nframes)
8350 rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
8351 ip2 = (Int *)calloc(sizeof(Int), nframes);
8352 ip = (Int *)calloc(sizeof(Int), nframes);
8353 for (i = 0; i < nframes; i++) {
8354 n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
8355 if (n < 0 || n >= nframes || ip2[n] != 0) {
8358 if (n < 0 || n >= nframes)
8359 rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
8361 rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
8367 MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
8372 #pragma mark ------ Fragments ------
8376 * fragment(n1, *exatoms) -> IntGroup
8377 * fragment(group, *exatoms) -> IntGroup
8379 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
8380 * those atoms will not be counted during the search.
8383 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
8386 IntGroup *baseg, *ig, *exatoms;
8388 volatile VALUE nval, exval;
8389 Data_Get_Struct(self, Molecule, mol);
8390 rb_scan_args(argc, argv, "1*", &nval, &exval);
8391 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
8393 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
8395 baseg = s_Molecule_AtomGroupFromValue(self, nval);
8397 if (RARRAY_LEN(exval) == 0) {
8400 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
8401 Data_Get_Struct(exval, IntGroup, exatoms);
8403 if (baseg == NULL) {
8404 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8406 IntGroupIterator iter;
8407 IntGroupIteratorInit(baseg, &iter);
8408 if ((n = IntGroupIteratorNext(&iter)) < 0) {
8411 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8413 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
8415 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
8417 IntGroupAddIntGroup(ig, subg);
8418 IntGroupRelease(subg);
8423 IntGroupIteratorRelease(&iter);
8424 IntGroupRelease(baseg);
8427 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
8428 nval = ValueFromIntGroup(ig);
8429 IntGroupRelease(ig);
8435 * fragments(exclude = nil)
8437 * Returns the fragments as an array of IntGroups.
8438 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8439 * in defining the fragment.
8442 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
8445 IntGroup *ag, *fg, *eg;
8446 VALUE gval, exval, retval;
8447 Data_Get_Struct(self, Molecule, mol);
8450 if (mol->natoms == 0)
8451 return rb_ary_new();
8452 rb_scan_args(argc, argv, "01", &exval);
8456 eg = IntGroupFromValue(exval);
8457 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8459 IntGroupRemoveIntGroup(ag, eg);
8460 retval = rb_ary_new();
8461 while (IntGroupGetCount(ag) > 0) {
8462 int n = IntGroupGetNthPoint(ag, 0);
8463 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8465 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8466 gval = ValueFromIntGroup(fg);
8467 rb_ary_push(retval, gval);
8468 IntGroupRemoveIntGroup(ag, fg);
8469 IntGroupRelease(fg);
8471 IntGroupRelease(ag);
8473 IntGroupRelease(eg);
8479 * each_fragment(exclude = nil) {|group| ...}
8481 * Execute the block, with the IntGroup object for each fragment as the argument.
8482 * Atoms or bonds should not be added or removed during the execution of the block.
8483 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
8484 * in defining the fragment.
8487 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
8490 IntGroup *ag, *fg, *eg;
8492 Data_Get_Struct(self, Molecule, mol);
8493 if (mol == NULL || mol->natoms == 0)
8495 rb_scan_args(argc, argv, "01", &exval);
8499 eg = IntGroupFromValue(exval);
8500 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
8502 IntGroupRemoveIntGroup(ag, eg);
8503 while (IntGroupGetCount(ag) > 0) {
8504 int n = IntGroupGetNthPoint(ag, 0);
8505 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
8507 rb_raise(rb_eMolbyError, "internal error during each_fragment");
8508 gval = ValueFromIntGroup(fg);
8510 IntGroupRemoveIntGroup(ag, fg);
8511 IntGroupRelease(fg);
8513 IntGroupRelease(ag);
8515 IntGroupRelease(eg);
8521 * detachable?(group) -> [n1, n2]
8523 * Check whether the group is 'detachable', i.e. the group is bound to the rest
8524 * of the molecule via only one bond. If it is, then the indices of the atoms
8525 * belonging to the bond is returned, the first element being the atom included
8526 * in the fragment. Otherwise, Qnil is returned.
8529 s_Molecule_Detachable_P(VALUE self, VALUE gval)
8535 Data_Get_Struct(self, Molecule, mol);
8536 ig = s_Molecule_AtomGroupFromValue(self, gval);
8537 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
8538 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
8539 } else retval = Qnil;
8540 IntGroupRelease(ig);
8546 * bonds_on_border(group = selection) -> Array of Array of two Integers
8548 * Returns an array of bonds that connect an atom in the group and an atom out
8549 * of the group. The first atom in the bond always belongs to the group. If no
8550 * such bonds are present, an empty array is returned.
8553 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
8558 Data_Get_Struct(self, Molecule, mol);
8559 rb_scan_args(argc, argv, "01", &gval);
8561 ig = MoleculeGetSelection(mol);
8565 ig = s_Molecule_AtomGroupFromValue(self, gval);
8567 retval = rb_ary_new();
8570 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
8572 IntGroupIterator iter;
8574 IntGroupIteratorInit(bg, &iter);
8575 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8576 /* The atoms at the border */
8578 n1 = mol->bonds[i * 2];
8579 n2 = mol->bonds[i * 2 + 1];
8580 if (IntGroupLookupPoint(ig, n1) < 0) {
8584 if (IntGroupLookupPoint(ig, n1) < 0)
8585 continue; /* Actually this is an internal error */
8587 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
8589 IntGroupIteratorRelease(&iter);
8591 IntGroupRelease(bg);
8592 IntGroupRelease(ig);
8596 /* Calculate the transform that moves the current coordinates to the reference
8597 coordinates with least displacements. */
8599 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8607 Double eigen_val[3];
8608 Vector eigen_vec[3];
8610 IntGroupIterator iter;
8612 natoms = mol->natoms;
8614 IntGroupIteratorInit(ig, &iter);
8616 /* Calculate the weighted center */
8620 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8621 ap1 = ATOM_AT_INDEX(ap, in);
8622 w1 = (weights != NULL ? weights[i] : ap1->weight);
8623 VecScaleInc(org1, ap1->r, w1);
8624 VecScaleInc(org2, ref[i], w1);
8628 VecScaleSelf(org1, w);
8629 VecScaleSelf(org2, w);
8631 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8632 /* Matrix to diagonalize = R * tR */
8633 memset(r, 0, sizeof(Mat33));
8634 memset(q, 0, sizeof(Mat33));
8635 memset(u, 0, sizeof(Mat33));
8637 IntGroupIteratorReset(&iter);
8638 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8640 ap1 = ATOM_AT_INDEX(ap, in);
8641 w1 = (weights != NULL ? weights[i] : ap1->weight);
8643 VecSub(v1, ap1->r, org1);
8644 VecSub(v2, ref[i], org2);
8645 r[0] += w1 * v1.x * v2.x;
8646 r[1] += w1 * v1.y * v2.x;
8647 r[2] += w1 * v1.z * v2.x;
8648 r[3] += w1 * v1.x * v2.y;
8649 r[4] += w1 * v1.y * v2.y;
8650 r[5] += w1 * v1.z * v2.y;
8651 r[6] += w1 * v1.x * v2.z;
8652 r[7] += w1 * v1.y * v2.z;
8653 r[8] += w1 * v1.z * v2.z;
8656 for (i = 0; i < 9; i++)
8658 for (i = 0; i < 3; i++) {
8659 for (j = 0; j < 3; j++) {
8660 for (k = 0; k < 3; k++) {
8661 q[i+j*3] += r[i+k*3] * r[j+k*3];
8666 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8667 IntGroupIteratorRelease(&iter);
8668 return -1.0; /* Cannot determine the eigenvector */
8671 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8672 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8673 MatrixTranspose(r, r);
8674 for (i = 0; i < 3; i++) {
8675 MatrixVec(&s[i], r, &eigen_vec[i]);
8676 w1 = 1.0 / sqrt(eigen_val[i]);
8677 VecScaleSelf(s[i], w1);
8679 for (k = 0; k < 3; k++) {
8680 u[0] += s[k].x * eigen_vec[k].x;
8681 u[1] += s[k].y * eigen_vec[k].x;
8682 u[2] += s[k].z * eigen_vec[k].x;
8683 u[3] += s[k].x * eigen_vec[k].y;
8684 u[4] += s[k].y * eigen_vec[k].y;
8685 u[5] += s[k].z * eigen_vec[k].y;
8686 u[6] += s[k].x * eigen_vec[k].z;
8687 u[7] += s[k].y * eigen_vec[k].z;
8688 u[8] += s[k].z * eigen_vec[k].z;
8691 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8692 MatrixVec(&org1, u, &org1);
8694 for (i = 0; i < 9; i++)
8700 /* Calculate rmsd */
8701 IntGroupIteratorReset(&iter);
8703 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8705 ap1 = ATOM_AT_INDEX(ap, in);
8706 TransformVec(&tv, trans, &ap1->r);
8708 w += VecLength2(tv);
8711 IntGroupIteratorRelease(&iter);
8717 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8719 * Calculate the transform to fit the given group to the set of reference coordinates.
8720 * The reference coordinates ref is given as either a frame number, an array of
8721 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8722 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8723 * Return values are the transform (that converts the present coordinates to the
8724 * target coordinates) and root mean square deviation (without weight).
8727 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8731 VALUE gval, rval, wval;
8733 IntGroupIterator iter;
8734 int nn, errnum, i, j, in, status;
8736 Double *weights, dval[3];
8739 Data_Get_Struct(self, Molecule, mol);
8740 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8742 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8744 ig = s_Molecule_AtomGroupFromValue(self, gval);
8745 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8746 IntGroupRelease(ig);
8747 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8749 ref = (Vector *)calloc(sizeof(Vector), nn);
8750 weights = (Double *)calloc(sizeof(Double), nn);
8751 IntGroupIteratorInit(ig, &iter);
8752 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8753 int fn = NUM2INT(rb_Integer(rval));
8754 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8759 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8760 ap = ATOM_AT_INDEX(mol->atoms, in);
8761 if (fn < ap->nframes)
8762 ref[i] = ap->frames[fn];
8763 else ref[i] = ap->r;
8765 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8766 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8767 if (m->row * m->column < nn * 3) {
8771 for (i = 0; i < nn; i++) {
8772 ref[i].x = m->data[i * 3];
8773 ref[i].y = m->data[i * 3 + 1];
8774 ref[i].z = m->data[i * 3 + 2];
8778 rval = rb_protect(rb_ary_to_ary, rval, &status);
8783 if (RARRAY_LEN(rval) < nn) {
8787 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8788 /* Array of 3*nn numbers */
8789 if (RARRAY_LEN(rval) < nn * 3) {
8793 for (i = 0; i < nn; i++) {
8794 for (j = 0; j < 3; j++) {
8795 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8800 dval[j] = NUM2DBL(aval);
8807 /* Array of nn Vector3Ds or Arrays */
8808 for (i = 0; i < nn; i++) {
8809 aval = (RARRAY_PTR(rval))[i];
8810 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8811 VectorFromValue(aval, &ref[i]);
8813 aval = rb_protect(rb_ary_to_ary, aval, &status);
8818 if (RARRAY_LEN(aval) < 3) {
8823 for (j = 0; j < 3; j++) {
8824 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8829 dval[j] = NUM2DBL(aaval);
8839 /* Use atomic weights */
8840 IntGroupIteratorReset(&iter);
8841 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8842 ap = ATOM_AT_INDEX(mol->atoms, in);
8843 weights[i] = ap->weight;
8846 wval = rb_protect(rb_ary_to_ary, wval, &status);
8851 if (RARRAY_LEN(wval) < nn) {
8855 for (i = 0; i < nn; i++) {
8856 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8861 weights[i] = NUM2DBL(wwval);
8864 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8871 IntGroupIteratorRelease(&iter);
8875 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8876 } else if (errnum == 1) {
8877 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8878 } else if (errnum == 2) {
8879 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8880 } else if (errnum == 3) {
8881 rb_jump_tag(status);
8882 } else if (errnum == 4) {
8883 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8884 } else if (errnum == 5) {
8885 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8886 } else if (errnum == 6) {
8887 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8889 return Qnil; /* Not reached */
8892 #pragma mark ------ Screen Display ------
8898 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8901 s_Molecule_Display(VALUE self)
8904 Data_Get_Struct(self, Molecule, mol);
8905 if (mol->mview != NULL)
8906 MainViewCallback_display(mol->mview);
8914 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8917 s_Molecule_MakeFront(VALUE self)
8920 Data_Get_Struct(self, Molecule, mol);
8921 if (mol->mview != NULL)
8922 MainViewCallback_makeFront(mol->mview);
8928 * update_enabled? -> bool
8930 * Returns true if screen update is enabled; otherwise no.
8933 s_Molecule_UpdateEnabled(VALUE self)
8936 Data_Get_Struct(self, Molecule, mol);
8937 if (mol->mview != NULL && !mol->mview->freezeScreen)
8944 * update_enabled = bool
8946 * Enable or disable screen update. This is effective for automatic update on modification.
8947 * Explicit call to molecule.display() always updates the screen.
8950 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8953 Data_Get_Struct(self, Molecule, mol);
8954 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8955 if (mol->mview != NULL)
8956 mol->mview->freezeScreen = (val == Qfalse);
8964 * show_unitcell(bool)
8965 * show_unitcell = bool
8967 * Set the flag whether to show the unit cell. If no argument is given, the
8968 * current flag is returned.
8971 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8974 Data_Get_Struct(self, Molecule, mol);
8975 if (mol->mview == NULL)
8978 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8979 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8981 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8987 * show_hydrogens(bool)
8988 * show_hydrogens = bool
8990 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8991 * current flag is returned.
8994 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8997 Data_Get_Struct(self, Molecule, mol);
8998 if (mol->mview == NULL)
9001 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
9002 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9004 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
9010 * show_dummy_atoms(bool)
9011 * show_dummy_atoms = bool
9013 * Set the flag whether to show the dummy atoms. If no argument is given, the
9014 * current flag is returned.
9017 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
9020 Data_Get_Struct(self, Molecule, mol);
9021 if (mol->mview == NULL)
9024 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
9025 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9027 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
9033 * show_expanded(bool)
9034 * show_expanded = bool
9036 * Set the flag whether to show the expanded atoms. If no argument is given, the
9037 * current flag is returned.
9040 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
9043 Data_Get_Struct(self, Molecule, mol);
9044 if (mol->mview == NULL)
9047 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
9048 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9050 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
9056 * show_ellipsoids(bool)
9057 * show_ellipsoids = bool
9059 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
9060 * current flag is returned.
9063 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
9066 Data_Get_Struct(self, Molecule, mol);
9067 if (mol->mview == NULL)
9070 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
9071 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9073 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
9078 * is_atom_visible(index) -> Boolean
9080 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
9081 * as well as the molecule attributes (showHydrogens, etc.)
9084 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9089 Data_Get_Struct(self, Molecule, mol);
9090 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9091 if (idx < 0 || idx >= mol->natoms)
9093 ap = ATOM_AT_INDEX(mol->atoms, idx);
9094 if (mol->mview != NULL) {
9095 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9097 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9099 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9102 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9107 * hidden_atoms -> IntGroup
9109 * Returns the currently hidden atoms.
9112 s_Molecule_HiddenAtoms(VALUE self)
9114 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9115 return Qnil; /* Not reached */
9120 * set_hidden_atoms(IntGroup)
9121 * self.hidden_atoms = IntGroup
9123 * Hide the specified atoms. This operation is _not_ undoable.
9126 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
9128 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
9129 return Qnil; /* Not reached */
9134 * show_graphite -> Integer
9135 * show_graphite = Integer
9136 * show_graphite = boolean
9138 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9139 * number of rings to display for each direction.
9140 * If the argument is boolean, only the show/hide flag is set.
9143 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9146 Data_Get_Struct(self, Molecule, mol);
9147 if (mol->mview == NULL)
9150 if (argv[0] == Qnil || argv[0] == Qfalse)
9151 mol->mview->showGraphiteFlag = 0;
9152 else if (argv[0] == Qtrue)
9153 mol->mview->showGraphiteFlag = 1;
9155 int n = NUM2INT(rb_Integer(argv[0]));
9157 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9158 mol->mview->showGraphite = n;
9160 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9162 return INT2NUM(mol->mview->showGraphite);
9167 * show_graphite? -> boolean
9169 * Return whether the graphite is set visible or not.
9172 s_Molecule_ShowGraphiteFlag(VALUE self)
9175 Data_Get_Struct(self, Molecule, mol);
9176 if (mol->mview == NULL)
9178 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9183 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9184 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9185 * show_periodic_image = boolean
9187 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9188 * set but no visual effects are observed.
9189 * If the argument is boolean, only the show/hide flag is modified.
9192 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9198 Data_Get_Struct(self, Molecule, mol);
9199 if (mol->mview == NULL)
9201 rb_scan_args(argc, argv, "01", &val);
9203 /* Change current settings */
9204 if (val == Qnil || val == Qfalse)
9205 mol->mview->showPeriodicImageFlag = 0;
9206 else if (val == Qtrue)
9207 mol->mview->showPeriodicImageFlag = 1;
9209 val = rb_ary_to_ary(val);
9210 for (i = 0; i < 6; i++) {
9211 if (i < RARRAY_LEN(val))
9212 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9214 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9215 rb_raise(rb_eMolbyError, "bad arguments");
9216 for (i = 0; i < 6; i++)
9217 mol->mview->showPeriodicImage[i] = ival[i];
9219 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9222 for (i = 0; i < 6; i++)
9223 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9229 * show_periodic_image? -> boolean
9231 * Return whether the periodic images are set to visible or not. This flag is
9232 * independent from the show_periodic_image settings.
9235 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9238 Data_Get_Struct(self, Molecule, mol);
9239 if (mol->mview == NULL)
9241 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9246 * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
9247 * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
9248 * show_rotation_center = boolean
9250 * Set to show the rotation center of the screen.
9251 * If the argument is boolean, only the show/hide flag is modified.
9254 s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
9257 Data_Get_Struct(self, Molecule, mol);
9258 if (mol->mview == NULL)
9261 mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
9262 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9264 return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
9273 * Set the flag whether to draw the model in line mode. If no argument is given, the
9274 * current flag is returned.
9277 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9280 Data_Get_Struct(self, Molecule, mol);
9281 if (mol->mview == NULL)
9284 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9285 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9287 return (mol->mview->lineMode ? Qtrue : Qfalse);
9292 * atom_radius = float
9295 * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
9297 * If no argument is given, the current value is returned.
9300 s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
9303 Data_Get_Struct(self, Molecule, mol);
9304 if (mol->mview == NULL)
9307 double rad = NUM2DBL(rb_Float(argv[0]));
9309 mol->mview->atomRadius = rad;
9310 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9314 return rb_float_new(mol->mview->atomRadius);
9319 * bond_radius = float
9322 * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
9323 * If no argument is given, the current value is returned.
9326 s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
9329 Data_Get_Struct(self, Molecule, mol);
9330 if (mol->mview == NULL)
9333 double rad = NUM2DBL(rb_Float(argv[0]));
9335 mol->mview->bondRadius = rad;
9336 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9340 return rb_float_new(mol->mview->bondRadius);
9345 * atom_resolution = integer
9348 * Set the atom resolution used in drawing the model in normal (non-line) mode.
9349 * (Default = 12; minimum = 6)
9350 * If no argument is given, the current value is returned.
9353 s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
9356 Data_Get_Struct(self, Molecule, mol);
9357 if (mol->mview == NULL)
9360 int res = NUM2INT(rb_Integer(argv[0]));
9362 rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
9363 mol->mview->atomResolution = res;
9364 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9365 return INT2NUM(res);
9367 return INT2NUM(mol->mview->atomResolution);
9372 * bond_resolution = integer
9375 * Set the bond resolution used in drawing the model in normal (non-line) mode.
9376 * (Default = 8; minimum = 4)
9377 * If no argument is given, the current value is returned.
9380 s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
9383 Data_Get_Struct(self, Molecule, mol);
9384 if (mol->mview == NULL)
9387 int res = NUM2INT(rb_Integer(argv[0]));
9389 rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
9390 mol->mview->bondResolution = res;
9391 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9392 return INT2NUM(res);
9394 return INT2NUM(mol->mview->bondResolution);
9401 * Resize the model drawing to fit in the window.
9404 s_Molecule_ResizeToFit(VALUE self)
9407 Data_Get_Struct(self, Molecule, mol);
9408 if (mol->mview != NULL)
9409 MainView_resizeToFit(mol->mview);
9415 * get_view_rotation -> [[ax, ay, az], angle]
9417 * Get the current rotation for the view. Angle is in degree, not radian.
9420 s_Molecule_GetViewRotation(VALUE self)
9425 Data_Get_Struct(self, Molecule, mol);
9426 if (mol->mview == NULL)
9428 TrackballGetRotate(mol->mview->track, f);
9429 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9433 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9438 * get_view_scale -> float
9440 * Get the current scale for the view.
9443 s_Molecule_GetViewScale(VALUE self)
9446 Data_Get_Struct(self, Molecule, mol);
9447 if (mol->mview == NULL)
9449 return rb_float_new(TrackballGetScale(mol->mview->track));
9454 * get_view_center -> Vector
9456 * Get the current center point of the view.
9459 s_Molecule_GetViewCenter(VALUE self)
9464 Data_Get_Struct(self, Molecule, mol);
9465 if (mol->mview == NULL)
9467 TrackballGetTranslate(mol->mview->track, f);
9468 v.x = -f[0] * mol->mview->dimension;
9469 v.y = -f[1] * mol->mview->dimension;
9470 v.z = -f[2] * mol->mview->dimension;
9471 return ValueFromVector(&v);
9476 * set_view_rotation([ax, ay, az], angle) -> self
9478 * Set the current rotation for the view. Angle is in degree, not radian.
9481 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9486 Data_Get_Struct(self, Molecule, mol);
9487 if (mol->mview == NULL)
9489 VectorFromValue(aval, &v);
9490 if (NormalizeVec(&v, &v))
9491 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9495 f[0] = -NUM2DBL(rb_Float(angval));
9496 TrackballSetRotate(mol->mview->track, f);
9497 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9503 * set_view_scale(scale) -> self
9505 * Set the current scale for the view.
9508 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9511 Data_Get_Struct(self, Molecule, mol);
9512 if (mol->mview == NULL)
9514 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9515 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9521 * set_view_center(vec) -> self
9523 * Set the current center point of the view.
9526 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9531 Data_Get_Struct(self, Molecule, mol);
9532 if (mol->mview == NULL)
9534 VectorFromValue(aval, &v);
9535 f[0] = -v.x / mol->mview->dimension;
9536 f[1] = -v.y / mol->mview->dimension;
9537 f[2] = -v.z / mol->mview->dimension;
9538 TrackballSetTranslate(mol->mview->track, f);
9539 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9545 * set_background_color(red, green, blue)
9547 * Set the background color of the model window.
9550 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9553 Data_Get_Struct(self, Molecule, mol);
9554 if (mol->mview != NULL) {
9555 VALUE rval, gval, bval;
9556 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9557 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9564 * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
9566 * Export the current graphic to a PNG or TIF file (determined by the extension).
9567 * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
9568 * If either width or height is not specified, then the screen width/height is used instead.
9571 s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
9574 VALUE fval, sval, bval, wval, hval;
9577 int bg_color, width, height;
9578 Data_Get_Struct(self, Molecule, mol);
9579 if (mol->mview == NULL)
9580 rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
9581 rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
9582 fname = FileStringValuePtr(fval);
9585 else scale = NUM2DBL(rb_Float(sval));
9588 else bg_color = NUM2INT(rb_Integer(bval));
9591 else width = NUM2INT(rb_Integer(wval));
9594 else height = NUM2INT(rb_Integer(hval));
9595 if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
9600 #pragma mark ------ Graphics ------
9603 s_CalculateGraphicNormals(MainViewGraphic *gp)
9607 if (gp == NULL || gp->npoints < 3)
9609 AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
9610 v1.x = gp->points[3] - gp->points[0];
9611 v1.y = gp->points[4] - gp->points[1];
9612 v1.z = gp->points[5] - gp->points[2];
9613 /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
9614 for (i = 2; i < gp->npoints; i++) {
9615 v2.x = gp->points[i * 3] - gp->points[0];
9616 v2.y = gp->points[i * 3 + 1] - gp->points[1];
9617 v2.z = gp->points[i * 3 + 2] - gp->points[2];
9618 VecCross(v3, v1, v2);
9619 NormalizeVec(&v3, &v3);
9620 gp->normals[i * 3] = v3.x;
9621 gp->normals[i * 3 + 1] = v3.y;
9622 gp->normals[i * 3 + 2] = v3.z;
9625 /* normals[0] = average of all nv[i] (i=2..n-1) */
9627 for (i = 2; i < gp->npoints; i++) {
9628 v1.x += gp->normals[i * 3];
9629 v1.y += gp->normals[i * 3 + 1];
9630 v1.z += gp->normals[i * 3 + 2];
9632 NormalizeVec(&v1, &v1);
9633 gp->normals[0] = v1.x;
9634 gp->normals[1] = v1.y;
9635 gp->normals[2] = v1.z;
9636 /* normals[1] = nv[2].normalize */
9637 v2.x = gp->normals[6];
9638 v2.y = gp->normals[7];
9639 v2.z = gp->normals[8];
9640 NormalizeVec(&v1, &v2);
9641 gp->normals[3] = v1.x;
9642 gp->normals[4] = v1.y;
9643 gp->normals[5] = v1.z;
9644 /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
9645 for (i = 2; i < gp->npoints; i++) {
9646 if (i == gp->npoints - 1)
9649 v3.x = gp->normals[i * 3 + 3];
9650 v3.y = gp->normals[i * 3 + 4];
9651 v3.z = gp->normals[i * 3 + 5];
9654 NormalizeVec(&v1, &v2);
9655 gp->normals[i * 3] = v1.x;
9656 gp->normals[i * 3 + 1] = v1.y;
9657 gp->normals[i * 3 + 2] = v1.z;
9664 * insert_graphic(index, kind, color, points, fill = nil) -> integer
9666 * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
9667 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9668 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9669 * points: an array of Vectors
9673 s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
9679 VALUE kval, cval, pval, fval, ival;
9680 Data_Get_Struct(self, Molecule, mol);
9681 if (mol->mview == NULL)
9682 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9683 rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
9684 idx = NUM2INT(rb_Integer(ival));
9686 idx = mol->mview->ngraphics;
9687 else if (idx < 0 || idx > mol->mview->ngraphics)
9688 rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
9689 memset(&g, 0, sizeof(g));
9691 if (rb_obj_is_kind_of(kval, rb_cInteger)) {
9692 g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
9694 kval = rb_obj_as_string(kval);
9695 p = StringValuePtr(kval);
9696 if (strcmp(p, "line") == 0)
9697 g.kind = kMainViewGraphicLine;
9698 else if (strcmp(p, "poly") == 0)
9699 g.kind = kMainViewGraphicPoly;
9700 else if (strcmp(p, "cylinder") == 0)
9701 g.kind = kMainViewGraphicCylinder;
9702 else if (strcmp(p, "cone") == 0)
9703 g.kind = kMainViewGraphicCone;
9704 else if (strcmp(p, "ellipsoid") == 0)
9705 g.kind = kMainViewGraphicEllipsoid;
9706 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9708 g.closed = (RTEST(fval) ? 1 : 0);
9709 cval = rb_ary_to_ary(cval);
9710 n = RARRAY_LEN(cval);
9711 if (n < 3 || n >= 5)
9712 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9715 for (i = 0; i < n; i++)
9716 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9717 pval = rb_ary_to_ary(pval);
9718 n = RARRAY_LEN(pval);
9719 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9721 rb_raise(rb_eArgError, "no control points are given");
9723 case kMainViewGraphicLine:
9725 rb_raise(rb_eArgError, "the line object must have at least two control points");
9727 case kMainViewGraphicPoly:
9729 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9731 case kMainViewGraphicCylinder:
9732 case kMainViewGraphicCone:
9734 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9737 case kMainViewGraphicEllipsoid:
9741 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9744 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9745 for (i = 0; i < n; i++) {
9747 VALUE rval = RARRAY_PTR(pval)[i];
9749 if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
9750 /* The float argument can also be given as a vector (for simplify undo registration) */
9751 VectorFromValue(rval, &v);
9753 v.x = NUM2DBL(rb_Float(rval));
9757 VectorFromValue(rval, &v);
9759 g.points[i * 3] = v.x;
9760 g.points[i * 3 + 1] = v.y;
9761 g.points[i * 3 + 2] = v.z;
9763 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9765 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9766 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9767 g.points[7] = g.points[11] = g.points[3];
9769 if (g.kind == kMainViewGraphicPoly) {
9770 /* Calculate normals */
9771 s_CalculateGraphicNormals(&g);
9773 MainView_insertGraphic(mol->mview, idx, &g);
9778 act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
9779 MolActionCallback_registerUndo(mol, act);
9780 MolActionRelease(act);
9783 return INT2NUM(idx);
9788 * create_graphic(kind, color, points, fill = nil) -> integer
9790 * Create a new graphic object. The arguments are similar as insert_graphic.
9793 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9796 rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
9797 args[0] = INT2NUM(-1);
9798 return s_Molecule_InsertGraphic(argc + 1, args, self);
9803 * remove_graphic(index) -> integer
9805 * Remove a graphic object.
9808 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9812 Data_Get_Struct(self, Molecule, mol);
9813 if (mol->mview == NULL)
9814 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9815 i = NUM2INT(rb_Integer(ival));
9816 if (i < 0 || i >= mol->mview->ngraphics)
9817 rb_raise(rb_eArgError, "graphic index is out of range");
9819 /* Prepare data for undo */
9820 MainViewGraphic *gp;
9825 gp = mol->mview->graphics + i;
9826 vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9827 for (n = 0; n < gp->npoints; n++) {
9828 vp[n].x = gp->points[n * 3];
9829 vp[n].y = gp->points[n * 3 + 1];
9830 vp[n].z = gp->points[n * 3 + 2];
9832 col[0] = gp->rgba[0];
9833 col[1] = gp->rgba[1];
9834 col[2] = gp->rgba[2];
9835 col[3] = gp->rgba[3];
9836 if (gp->visible == 0) {
9837 act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
9838 MolActionCallback_registerUndo(mol, act);
9839 MolActionRelease(act);
9841 act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
9842 MolActionCallback_registerUndo(mol, act);
9844 MolActionRelease(act);
9846 MainView_removeGraphic(mol->mview, i);
9852 * ngraphics -> integer
9854 * Get the number of graphic objects.
9857 s_Molecule_NGraphics(VALUE self)
9860 Data_Get_Struct(self, Molecule, mol);
9861 if (mol->mview == NULL)
9862 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9863 return INT2NUM(mol->mview->ngraphics);
9868 * get_graphic_point(graphic_index, point_index) -> value
9869 * get_graphic_points(graphic_index) -> values
9871 * Get the point_index-th control point of graphic_index-th graphic object.
9872 * Get an array of all control points with the given values.
9876 s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
9878 MainViewGraphic *gp;
9883 Data_Get_Struct(self, Molecule, mol);
9884 if (mol->mview == NULL)
9885 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9886 rb_scan_args(argc, argv, "11", &gval, &pval);
9887 index = NUM2INT(rb_Integer(gval));
9888 if (index < 0 || index >= mol->mview->ngraphics)
9889 rb_raise(rb_eArgError, "the graphic index is out of range");
9890 gp = mol->mview->graphics + index;
9892 pindex = NUM2INT(rb_Integer(pval));
9893 if (pindex < 0 || pindex >= gp->npoints)
9894 rb_raise(rb_eArgError, "the point index is out of range");
9895 v.x = gp->points[pindex * 3];
9896 v.y = gp->points[pindex * 3 + 1];
9897 v.z = gp->points[pindex * 3 + 2];
9898 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9899 return rb_float_new(v.x);
9901 return ValueFromVector(&v);
9904 pval = rb_ary_new();
9905 for (pindex = 0; pindex < gp->npoints; pindex++) {
9906 v.x = gp->points[pindex * 3];
9907 v.y = gp->points[pindex * 3 + 1];
9908 v.z = gp->points[pindex * 3 + 2];
9909 rb_ary_push(pval, ValueFromVector(&v));
9917 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9918 * set_graphic_points(graphic_index, new_values) -> new_values
9920 * Change the point_index-th control point of graphic_index-th graphic object.
9921 * Replace the control points with the given values.
9925 s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
9927 MainViewGraphic *gp;
9931 VALUE gval, pval, nval;
9933 Data_Get_Struct(self, Molecule, mol);
9934 if (mol->mview == NULL)
9935 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9936 rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
9937 index = NUM2INT(rb_Integer(gval));
9938 if (index < 0 || index >= mol->mview->ngraphics)
9939 rb_raise(rb_eArgError, "the graphic index is out of range");
9940 gp = mol->mview->graphics + index;
9942 pindex = NUM2INT(rb_Integer(pval));
9943 if (pindex < 0 || pindex >= gp->npoints)
9944 rb_raise(rb_eArgError, "the point index is out of range");
9945 v0.x = gp->points[pindex * 3];
9946 v0.y = gp->points[pindex * 3 + 1];
9947 v0.z = gp->points[pindex * 3 + 2];
9948 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9949 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
9950 v.x = NUM2DBL(rb_Float(nval));
9952 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
9953 v.x = NUM2DBL(rb_Float(nval));
9955 gp->points[7] = gp->points[11] = v.x;
9956 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9957 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9960 v.x = kInvalidFloat;
9962 } else VectorFromValue(nval, &v);
9964 gp->points[pindex * 3] = v.x;
9965 gp->points[pindex * 3 + 1] = v.y;
9966 gp->points[pindex * 3 + 2] = v.z;
9967 act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
9971 Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
9972 for (pindex = 0; pindex < gp->npoints; pindex++) {
9973 vp[pindex].x = gp->points[pindex * 3];
9974 vp[pindex].y = gp->points[pindex * 3 + 1];
9975 vp[pindex].z = gp->points[pindex * 3 + 2];
9977 act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
9979 pval = rb_ary_to_ary(pval);
9980 len = RARRAY_LEN(pval);
9981 if (gp->npoints < len) {
9982 gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
9984 } else if (gp->npoints > len) {
9987 case kMainViewGraphicLine: len2 = 2; break;
9988 case kMainViewGraphicPoly: len2 = 3; break;
9989 case kMainViewGraphicCylinder: len2 = 3; break;
9990 case kMainViewGraphicCone: len2 = 3; break;
9991 case kMainViewGraphicEllipsoid: len2 = 4; break;
9997 for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
9998 aval = RARRAY_PTR(pval)[pindex];
9999 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
10000 v.x = NUM2DBL(rb_Float(aval));
10002 } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
10003 v.x = NUM2DBL(rb_Float(aval));
10005 gp->points[7] = gp->points[11] = v.x;
10006 gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
10008 } else VectorFromValue(aval, &v);
10009 gp->points[pindex * 3] = v.x;
10010 gp->points[pindex * 3 + 1] = v.y;
10011 gp->points[pindex * 3 + 2] = v.z;
10014 if (gp->kind == kMainViewGraphicPoly) {
10015 /* Calculate normals */
10016 s_CalculateGraphicNormals(gp);
10018 MolActionCallback_registerUndo(mol, act);
10019 MolActionRelease(act);
10020 MoleculeCallback_notifyModification(mol, 0);
10026 * get_graphic_color(graphic_index) -> value
10028 * Get the color of graphic_index-th graphic object
10031 s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
10033 MainViewGraphic *gp;
10036 Data_Get_Struct(self, Molecule, mol);
10037 if (mol->mview == NULL)
10038 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10039 index = NUM2INT(rb_Integer(gval));
10040 if (index < 0 || index >= mol->mview->ngraphics)
10041 rb_raise(rb_eArgError, "the graphic index is out of range");
10042 gp = mol->mview->graphics + index;
10043 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]));
10048 * set_graphic_color(graphic_index, new_value) -> new_value
10050 * Change the color of graphic_index-th graphic object
10054 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
10056 MainViewGraphic *gp;
10061 Data_Get_Struct(self, Molecule, mol);
10062 if (mol->mview == NULL)
10063 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10064 index = NUM2INT(rb_Integer(gval));
10065 if (index < 0 || index >= mol->mview->ngraphics)
10066 rb_raise(rb_eArgError, "the graphic index is out of range");
10067 gp = mol->mview->graphics + index;
10068 for (i = 0; i < 4; i++)
10069 c[i] = gp->rgba[i];
10070 cval = rb_ary_to_ary(cval);
10071 n = RARRAY_LEN(cval);
10072 if (n != 3 && n != 4)
10073 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
10075 for (i = 0; i < n; i++) {
10076 gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
10080 act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
10081 MolActionCallback_registerUndo(mol, act);
10082 MolActionRelease(act);
10083 MoleculeCallback_notifyModification(mol, 0);
10089 * show_graphic(graphic_index) -> self
10091 * Enable the visible flag of the graphic_index-th graphic object
10095 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
10097 MainViewGraphic *gp;
10100 Data_Get_Struct(self, Molecule, mol);
10101 if (mol->mview == NULL)
10102 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10103 index = NUM2INT(rb_Integer(gval));
10104 if (index < 0 || index >= mol->mview->ngraphics)
10105 rb_raise(rb_eArgError, "the graphic index is out of range");
10106 gp = mol->mview->graphics + index;
10108 MoleculeCallback_notifyModification(mol, 0);
10114 * hide_graphic(graphic_index) -> self
10116 * Disable the visible flag of the graphic_index-th graphic object
10120 s_Molecule_HideGraphic(VALUE self, VALUE gval)
10122 MainViewGraphic *gp;
10125 Data_Get_Struct(self, Molecule, mol);
10126 if (mol->mview == NULL)
10127 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
10128 index = NUM2INT(rb_Integer(gval));
10129 if (index < 0 || index >= mol->mview->ngraphics)
10130 rb_raise(rb_eArgError, "the graphic index is out of range");
10131 gp = mol->mview->graphics + index;
10133 MoleculeCallback_notifyModification(mol, 0);
10139 * show_text(string)
10141 * Show the string in the info text box.
10144 s_Molecule_ShowText(VALUE self, VALUE arg)
10147 Data_Get_Struct(self, Molecule, mol);
10148 if (mol->mview != NULL)
10149 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
10153 #pragma mark ------ MD Support ------
10157 * md_arena -> MDArena
10159 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
10160 * this molecule, a new arena is created.
10163 s_Molecule_MDArena(VALUE self)
10167 Data_Get_Struct(self, Molecule, mol);
10168 if (mol->arena == NULL)
10170 retval = ValueFromMDArena(mol->arena);
10176 * set_parameter_attr(type, index, key, value, src) -> value
10178 * This method is used only internally.
10181 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
10183 /* This method is called from MolAction to change a MM parameter attribute. */
10186 ParameterRef *pref;
10188 Data_Get_Struct(self, Molecule, mol);
10189 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
10190 vval = s_ParameterRef_SetAttr(pval, kval, vval);
10192 /* This is the special part of this method; it allows modification of the src field. */
10193 /* (ParameterRef#set_attr sets 0 to the src field) */
10194 Data_Get_Struct(pval, ParameterRef, pref);
10195 up = ParameterRefGetPar(pref);
10196 up->bond.src = FIX2INT(sval);
10203 * parameter -> Parameter
10205 * Get the local parameter of this molecule. If not defined, returns nil.
10208 s_Molecule_Parameter(VALUE self)
10211 Data_Get_Struct(self, Molecule, mol);
10212 /* if (mol->par == NULL)
10214 return s_NewParameterValueFromValue(self);
10219 * start_step -> Integer
10221 * Returns the start step (defined by dcd format).
10224 s_Molecule_StartStep(VALUE self)
10227 Data_Get_Struct(self, Molecule, mol);
10228 return INT2NUM(mol->startStep);
10233 * start_step = Integer
10235 * Set the start step (defined by dcd format).
10238 s_Molecule_SetStartStep(VALUE self, VALUE val)
10241 Data_Get_Struct(self, Molecule, mol);
10242 mol->startStep = NUM2INT(rb_Integer(val));
10248 * steps_per_frame -> Integer
10250 * Returns the number of steps between frames (defined by dcd format).
10253 s_Molecule_StepsPerFrame(VALUE self)
10256 Data_Get_Struct(self, Molecule, mol);
10257 return INT2NUM(mol->stepsPerFrame);
10262 * steps_per_frame = Integer
10264 * Set the number of steps between frames (defined by dcd format).
10267 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
10270 Data_Get_Struct(self, Molecule, mol);
10271 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
10277 * ps_per_step -> Float
10279 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
10282 s_Molecule_PsPerStep(VALUE self)
10285 Data_Get_Struct(self, Molecule, mol);
10286 return rb_float_new(mol->psPerStep);
10291 * ps_per_step = Float
10293 * Set the time increment (in picoseconds) for one step (defined by dcd format).
10296 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
10299 Data_Get_Struct(self, Molecule, mol);
10300 mol->psPerStep = NUM2DBL(rb_Float(val));
10305 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
10307 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.");
10310 #pragma mark ------ MO Handling ------
10314 * selectedMO -> IntGroup
10316 * Returns a group of selected mo in the "MO Info" table. If the MO info table
10317 * is not selected, returns nil. If the MO info table is selected but no MOs
10318 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
10321 s_Molecule_SelectedMO(VALUE self)
10326 Data_Get_Struct(self, Molecule, mol);
10327 if (mol->mview == NULL)
10329 ig = MainView_selectedMO(mol->mview);
10332 IntGroupOffset(ig, 1);
10333 val = ValueFromIntGroup(ig);
10334 IntGroupRelease(ig);
10340 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
10342 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
10343 * If the molecule does not contain a basis set information, then returns nil.
10346 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
10349 Vector o, dx, dy, dz;
10352 Int npoints = 80 * 80 * 80;
10353 Data_Get_Struct(self, Molecule, mol);
10354 if (mol->bset == NULL)
10356 rb_scan_args(argc, argv, "01", &nval);
10358 npoints = NUM2INT(rb_Integer(nval));
10359 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10361 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));
10365 s_Cubegen_callback(double progress, void *ref)
10367 MyAppCallback_setProgressValue(progress);
10368 if (MyAppCallback_checkInterrupt())
10375 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
10376 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
10378 * Calculate the molecular orbital with number mo and create a 'cube' file.
10379 * In the first form, the cube size is estimated from the atomic coordinates. In the
10380 * second form, the cube dimension is explicitly given.
10381 * Returns fname when successful, nil otherwise.
10382 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
10383 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
10384 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
10387 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
10389 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
10391 Int mono, nx, ny, nz, npoints;
10392 Vector o, dx, dy, dz;
10395 Data_Get_Struct(self, Molecule, mol);
10396 if (mol->bset == NULL)
10397 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
10398 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
10400 /* Set up parameters */
10401 mono = NUM2INT(rb_Integer(mval));
10402 if (mono < 0 || mono > mol->bset->ncomps)
10403 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);
10405 if (mol->bset->rflag != 0)
10406 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
10407 mono += mol->bset->ncomps;
10410 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
10411 /* Automatic grid formation */
10413 npoints = NUM2INT(rb_Integer(oval));
10417 else if (npoints < 8)
10418 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
10419 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10420 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
10424 VectorFromValue(oval, &o);
10425 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
10426 VectorFromValue(dxval, &dx);
10428 dx.x = NUM2DBL(rb_Float(dxval));
10431 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
10432 VectorFromValue(dyval, &dy);
10434 dy.y = NUM2DBL(rb_Float(dyval));
10437 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
10438 VectorFromValue(dzval, &dz);
10440 dz.z = NUM2DBL(rb_Float(dzval));
10443 nx = NUM2INT(rb_Integer(nxval));
10444 ny = NUM2INT(rb_Integer(nyval));
10445 nz = NUM2INT(rb_Integer(nzval));
10446 if (nx <= 0 || ny <= 0 || nz <= 0)
10447 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
10448 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
10449 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);
10453 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
10456 else if (index < 0)
10457 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
10459 /* Output to file */
10460 MoleculeCallback_displayName(mol, buf, sizeof buf);
10461 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
10463 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
10465 /* Discard the cube */
10466 MoleculeClearCubeAtIndex(mol, index);
10474 * Clear the MO surface if present.
10477 s_Molecule_ClearSurface(VALUE self)
10480 Data_Get_Struct(self, Molecule, mol);
10481 if (mol->mcube != NULL)
10482 MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
10490 * Hide the MO surface if present.
10493 s_Molecule_HideSurface(VALUE self)
10496 Data_Get_Struct(self, Molecule, mol);
10497 if (mol->mcube != NULL) {
10498 mol->mcube->hidden = 1;
10499 MoleculeCallback_notifyModification(mol, 0);
10508 * Show the MO surface if present.
10511 s_Molecule_ShowSurface(VALUE self)
10514 Data_Get_Struct(self, Molecule, mol);
10515 if (mol->mcube != NULL) {
10516 mol->mcube->hidden = 0;
10517 MoleculeCallback_notifyModification(mol, 0);
10524 * create_surface(mo, attr = nil)
10526 * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
10527 * then it denotes the beta orbital.
10528 * If mo is nil, then the attributes of the current surface are modified.
10530 * :npoints : the approximate number of grid points
10531 * :expand : the scale factor to expand/shrink the display box size for each atom,
10532 * :thres : the threshold for the isovalue surface
10533 * If the molecule does not contain MO information, raises exception.
10536 s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
10539 Vector o, dx, dy, dz;
10540 Int nmo, nx, ny, nz, i;
10541 Int need_recalc = 0;
10542 VALUE nval, hval, aval;
10547 Data_Get_Struct(self, Molecule, mol);
10548 rb_scan_args(argc, argv, "11", &nval, &hval);
10549 if (mol->bset == NULL)
10550 rb_raise(rb_eMolbyError, "No MO information is given");
10551 if (nval == Qnil) {
10553 } else if (nval == ID2SYM(rb_intern("total_density"))) {
10554 nmo = mol->bset->nmos + 1;
10556 nmo = NUM2INT(rb_Integer(nval));
10557 if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
10558 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);
10560 nmo = -nmo + mol->bset->ncomps;
10562 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
10563 npoints = NUM2INT(rb_Integer(aval));
10565 } else if (mol->mcube != NULL) {
10566 npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
10567 } else npoints = 80 * 80 * 80;
10568 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
10569 expand = NUM2DBL(rb_Float(aval));
10570 } else if (mol->mcube != NULL) {
10571 expand = mol->mcube->expand;
10572 } else expand = 1.0;
10573 if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
10574 thres = NUM2DBL(rb_Float(aval));
10575 } else if (mol->mcube != NULL) {
10576 thres = mol->mcube->thres;
10577 } else thres = 0.05;
10578 if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
10579 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
10580 rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
10581 if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
10582 rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
10584 for (nx = 0; nx < 2; nx++) {
10585 aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
10586 if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
10587 aval = rb_ary_to_ary(aval);
10588 if (RARRAY_LEN(aval) < 3) {
10590 rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
10592 for (i = 0; i < 4; i++)
10593 d[i] = mol->mcube->c[nx].rgba[i];
10594 for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
10595 d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10596 if (d[i] < 0.0 && d[i] > 1.0)
10599 for (i = 0; i < 4; i++)
10600 mol->mcube->c[nx].rgba[i] = d[i];
10603 if (mol->mcube->expand != expand)
10605 mol->mcube->thres = thres;
10606 mol->mcube->expand = expand;
10608 if (mol->mcube->idn < 0)
10609 return self; /* Only set attributes for now */
10611 nmo = mol->mcube->idn; /* Force recalculation */
10613 if (MoleculeUpdateMCube(mol, nmo) != 0)
10614 rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
10620 * set_surface_attr(attr = nil)
10622 * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
10625 s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
10630 return s_Molecule_CreateSurface(2, args, self);
10637 * Get the number of electrostatic potential info.
10640 s_Molecule_NElpots(VALUE self)
10643 Data_Get_Struct(self, Molecule, mol);
10644 return INT2NUM(mol->nelpots);
10651 * Get the electrostatic potential info at the given index. If present, then the
10652 * return value is [Vector, Float] (position and potential). If not present, then
10656 s_Molecule_Elpot(VALUE self, VALUE ival)
10660 Data_Get_Struct(self, Molecule, mol);
10661 idx = NUM2INT(rb_Integer(ival));
10662 if (idx < 0 || idx >= mol->nelpots)
10664 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
10671 * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
10672 * cube and marching cube information are discarded. This operation is _not_ undoable!
10675 s_Molecule_ClearBasisSet(VALUE self)
10678 Data_Get_Struct(self, Molecule, mol);
10680 if (mol->bset != NULL) {
10681 BasisSetRelease(mol->bset);
10684 if (mol->mcube != NULL) {
10685 MoleculeDeallocateMCube(mol->mcube);
10694 * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives[, additional_exponent])
10696 * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
10697 * and the number of primitives.
10698 * Additional exponent is for JANPA only; implements an additinal r^N component that
10699 * appears in cartesian->spherical conversion.
10700 * Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type; -2, D5-type;
10701 * 3, F-type; -3, F7-type; 4, G-type; -4, G9-type.
10702 * Or: "s", S-type; "p", P-type; "sp", SP-type; "d", D-type; "d5", D5-type;
10703 * "f", F-type; "f7", F7-type; "g", G-type; "g9", G9-type
10706 s_Molecule_AddGaussianOrbitalShell(int argc, VALUE *argv, VALUE self)
10709 int sym, nprims, a_idx, n, add_exp;
10710 VALUE aval, symval, npval, addval;
10711 Data_Get_Struct(self, Molecule, mol);
10712 rb_scan_args(argc, argv, "31", &aval, &symval, &npval, &addval);
10713 if (rb_obj_is_kind_of(symval, rb_cString)) {
10714 const char *p = StringValuePtr(symval);
10715 if (strcasecmp(p, "s") == 0)
10717 else if (strcasecmp(p, "p") == 0)
10719 else if (strcasecmp(p, "sp") == 0)
10721 else if (strcasecmp(p, "d") == 0)
10723 else if (strcasecmp(p, "d5") == 0)
10725 else if (strcasecmp(p, "f") == 0)
10727 else if (strcasecmp(p, "f7") == 0)
10729 else if (strcasecmp(p, "g") == 0)
10731 else if (strcasecmp(p, "g9") == 0)
10734 rb_raise(rb_eArgError, "Unknown orbital type '%s'", p);
10736 sym = NUM2INT(rb_Integer(symval));
10738 a_idx = NUM2INT(rb_Integer(aval));
10739 nprims = NUM2INT(rb_Integer(npval));
10740 if (addval != Qnil)
10741 add_exp = NUM2INT(rb_Integer(addval));
10743 n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims, add_exp);
10745 rb_raise(rb_eMolbyError, "Molecule is emptry");
10747 rb_raise(rb_eMolbyError, "Low memory");
10749 rb_raise(rb_eMolbyError, "Unknown orbital type");
10751 rb_raise(rb_eMolbyError, "Unknown error");
10757 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
10759 * To be used internally. Add a gaussian primitive coefficients.
10762 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
10766 Double exponent, contraction, contraction_sp;
10767 Data_Get_Struct(self, Molecule, mol);
10768 exponent = NUM2DBL(rb_Float(expval));
10769 contraction = NUM2DBL(rb_Float(cval));
10770 contraction_sp = NUM2DBL(rb_Float(cspval));
10771 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
10773 rb_raise(rb_eMolbyError, "Molecule is emptry");
10775 rb_raise(rb_eMolbyError, "Low memory");
10777 rb_raise(rb_eMolbyError, "Unknown error");
10783 * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
10785 * Get the Gaussian shell information for the given MO coefficient index.
10786 * The symmetry code is the same as in add_gaussian_orbital_shell.
10787 * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
10788 * is the number of MO component belonging to this shell.
10791 s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
10796 Data_Get_Struct(self, Molecule, mol);
10797 if (mol->bset == NULL)
10798 rb_raise(rb_eMolbyError, "No basis set information is defined");
10799 s_idx = NUM2INT(rb_Integer(sval));
10800 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10802 sp = mol->bset->shells + s_idx;
10805 case kGTOType_S: sym = 0; break;
10806 case kGTOType_SP: sym = -1; break;
10807 case kGTOType_P: sym = 1; break;
10808 case kGTOType_D: sym = 2; break;
10809 case kGTOType_D5: sym = -2; break;
10810 case kGTOType_F: sym = 3; break;
10811 case kGTOType_F7: sym = -3; break;
10812 case kGTOType_G: sym = 4; break;
10813 case kGTOType_G9: sym = -4; break;
10815 rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
10817 return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
10822 * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
10824 * Get the Gaussian primitive coefficients for the given MO component.
10827 s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
10833 VALUE retval, aval;
10834 Data_Get_Struct(self, Molecule, mol);
10835 if (mol->bset == NULL)
10836 rb_raise(rb_eMolbyError, "No basis set information is defined");
10837 s_idx = NUM2INT(rb_Integer(sval));
10838 if (s_idx < 0 || s_idx >= mol->bset->nshells)
10840 sp = mol->bset->shells + s_idx;
10841 pp = mol->bset->priminfos + sp->p_idx;
10842 retval = rb_ary_new2(sp->nprim);
10843 for (i = 0; i < sp->nprim; i++) {
10844 if (sp->sym == kGTOType_SP) {
10845 /* With P contraction coefficient */
10846 aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
10848 /* Without P contraction coefficient */
10849 aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
10851 rb_ary_store(retval, i, aval);
10858 * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
10860 * Get the Gaussian shell information for the given MO coefficient index.
10863 s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
10866 Int n, c, atom_idx, shell_idx;
10868 Data_Get_Struct(self, Molecule, mol);
10869 if (mol->bset == NULL)
10870 rb_raise(rb_eMolbyError, "No basis set information is defined");
10871 c = NUM2INT(rb_Integer(cval));
10872 if (c < 0 || c >= mol->bset->ncomps)
10874 n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
10876 rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
10877 return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
10882 * clear_mo_coefficients
10884 * Clear the existing MO coefficients.
10887 s_Molecule_ClearMOCoefficients(VALUE self)
10890 Data_Get_Struct(self, Molecule, mol);
10891 if (mol->bset != NULL) {
10892 if (mol->bset->moenergies != NULL) {
10893 free(mol->bset->moenergies);
10894 mol->bset->moenergies = NULL;
10896 if (mol->bset->mo != NULL) {
10897 free(mol->bset->mo);
10898 mol->bset->mo = NULL;
10900 mol->bset->nmos = 0;
10907 * set_mo_coefficients(idx, energy, coefficients)
10909 * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
10910 * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
10911 * Energy is the MO energy, and coefficients is an array
10912 * of MO coefficients.
10915 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
10918 Int idx, ncomps, i;
10921 Data_Get_Struct(self, Molecule, mol);
10922 idx = NUM2INT(rb_Integer(ival));
10923 energy = NUM2DBL(rb_Float(eval));
10924 aval = rb_ary_to_ary(aval);
10925 ncomps = RARRAY_LEN(aval);
10926 coeffs = (Double *)calloc(sizeof(Double), ncomps);
10927 if (coeffs == NULL) {
10931 for (i = 0; i < ncomps; i++)
10932 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10933 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
10936 rb_raise(rb_eMolbyError, "Molecule is emptry");
10938 rb_raise(rb_eMolbyError, "Low memory");
10940 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
10942 rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
10944 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
10946 rb_raise(rb_eMolbyError, "Unknown error");
10952 * get_mo_coefficients(idx)
10954 * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
10957 s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
10960 Int idx, ncomps, n;
10964 Data_Get_Struct(self, Molecule, mol);
10965 idx = NUM2INT(rb_Integer(ival));
10968 n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
10970 rb_raise(rb_eMolbyError, "Molecule is emptry");
10972 rb_raise(rb_eMolbyError, "No basis set information is present");
10974 return Qnil; /* Silently returns nil */
10975 retval = rb_ary_new2(ncomps);
10976 for (n = 0; n < ncomps; n++)
10977 rb_ary_store(retval, n, rb_float_new(coeffs[n]));
10984 * get_mo_energy(idx)
10986 * To be used internally. Get the MO energy for the given MO index (1-based).
10989 s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
10994 Data_Get_Struct(self, Molecule, mol);
10995 idx = NUM2INT(rb_Integer(ival));
10996 n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
10998 rb_raise(rb_eMolbyError, "Molecule is emptry");
11000 rb_raise(rb_eMolbyError, "No basis set information is present");
11003 return rb_float_new(energy);
11006 static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
11009 s_InitMOInfoKeys(void)
11011 if (sTypeSym == 0) {
11012 sTypeSym = ID2SYM(rb_intern("type"));
11013 sAlphaSym = ID2SYM(rb_intern("alpha"));
11014 sBetaSym = ID2SYM(rb_intern("beta"));
11015 sNcompsSym = ID2SYM(rb_intern("ncomps"));
11016 sNshellsSym = ID2SYM(rb_intern("nshells"));
11022 * set_mo_info(hash)
11024 * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
11025 * :alpha=>integer, :beta=>integer
11028 s_Molecule_SetMOInfo(VALUE self, VALUE hval)
11032 Int rflag, na, nb, n;
11034 Data_Get_Struct(self, Molecule, mol);
11035 if (mol->bset != NULL) {
11036 rflag = mol->bset->rflag;
11037 na = mol->bset->ne_alpha;
11038 nb = mol->bset->ne_beta;
11044 if (hval != Qnil) {
11045 if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
11046 s = StringValuePtr(aval);
11047 if (strcasecmp(s, "RHF") == 0)
11049 else if (strcasecmp(s, "UHF") == 0)
11051 else if (strcasecmp(s, "ROHF") == 0)
11054 if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
11055 n = NUM2INT(rb_Integer(aval));
11059 if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
11060 n = NUM2INT(rb_Integer(aval));
11064 MoleculeSetMOInfo(mol, rflag, na, nb);
11073 * Get the MO info. The key is as described in set_mo_info.
11074 * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
11077 s_Molecule_GetMOInfo(VALUE self, VALUE kval)
11080 Data_Get_Struct(self, Molecule, mol);
11081 if (mol->bset == NULL)
11083 if (kval == sTypeSym) {
11084 switch (mol->bset->rflag) {
11085 case 0: return Ruby_NewEncodedStringValue2("UHF");
11086 case 1: return Ruby_NewEncodedStringValue2("RHF");
11087 case 2: return Ruby_NewEncodedStringValue2("ROHF");
11088 default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
11090 } else if (kval == sAlphaSym) {
11091 return INT2NUM(mol->bset->ne_alpha);
11092 } else if (kval == sBetaSym) {
11093 return INT2NUM(mol->bset->ne_beta);
11094 } else if (kval == sNcompsSym) {
11095 return INT2NUM(mol->bset->ncomps);
11096 } else if (kval == sNshellsSym) {
11097 return INT2NUM(mol->bset->nshells);
11099 kval = rb_inspect(kval);
11100 rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
11101 return Qnil; /* Does not reach here */
11109 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
11112 s_Molecule_MOType(VALUE self)
11114 return s_Molecule_GetMOInfo(self, sTypeSym);
11117 #pragma mark ------ Molecular Topology ------
11121 * search_equivalent_atoms(ig = nil)
11123 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
11126 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
11132 Data_Get_Struct(self, Molecule, mol);
11133 if (mol->natoms == 0)
11135 rb_scan_args(argc, argv, "01", &val);
11137 ig = s_Molecule_AtomGroupFromValue(self, val);
11139 result = MoleculeSearchEquivalentAtoms(mol, ig);
11140 if (result == NULL)
11141 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
11143 IntGroupRelease(ig);
11144 val = rb_ary_new2(mol->natoms);
11145 for (i = 0; i < mol->natoms; i++)
11146 rb_ary_push(val, INT2NUM(result[i]));
11153 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
11155 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
11156 * Name is the name of the new pi anchor, and group is the atoms that define
11157 * the pi system. Type (a String) is an atom type for MM implementation.
11158 * Weights represent the relative significance of the component atoms; if omitted, then
11159 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
11160 * The weight values will be normalized so that the sum of the weights is 1.0.
11161 * The weight values must be positive.
11162 * Index is the atom index where the created pi-anchor is inserted in the
11163 * atoms array; if omitted, the pi-anchor is inserted after the component atom
11164 * having the largest index.
11165 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
11166 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
11169 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
11174 Int i, n, idx, last_component;
11178 if (argc < 2 || argc >= 6)
11179 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
11183 Data_Get_Struct(self, Molecule, mol);
11187 ig = s_Molecule_AtomGroupFromValue(self, gval);
11188 if (ig == NULL || IntGroupGetCount(ig) == 0)
11189 rb_raise(rb_eMolbyError, "atom group is not given correctly");
11190 memset(&a, 0, sizeof(a));
11191 memset(&an, 0, sizeof(an));
11192 strncpy(a.aname, StringValuePtr(nval), 4);
11193 if (a.aname[0] == '_')
11194 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
11195 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
11196 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
11197 if (n >= mol->natoms) {
11198 AtomConnectResize(&an.connect, 0);
11199 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
11201 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
11202 last_component = n;
11204 if (an.connect.count == 0)
11205 rb_raise(rb_eMolbyError, "no atoms are specified");
11206 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
11207 for (i = 0; i < an.connect.count; i++) {
11208 an.coeffs[i] = 1.0 / an.connect.count;
11210 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
11212 if (argv[0] != Qnil)
11213 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
11217 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
11218 if (argv[0] != Qnil) {
11219 VALUE aval = rb_ary_to_ary(argv[0]);
11221 if (RARRAY_LEN(aval) != an.connect.count)
11222 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
11223 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
11224 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
11226 rb_raise(rb_eMolbyError, "the weight value must be positive");
11230 for (i = 0; i < an.connect.count; i++)
11231 an.coeffs[i] /= sum;
11236 if (argc > 0 && argv[0] != Qnil) {
11238 idx = NUM2INT(rb_Integer(argv[0]));
11240 if (idx < 0 || idx > mol->natoms) {
11241 /* Immediately after the last specified atom */
11242 idx = last_component + 1;
11244 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
11245 memmove(a.anchor, &an, sizeof(PiAnchor));
11246 /* Use residue information of the last specified atom */
11247 ap = ATOM_AT_INDEX(mol->atoms, last_component);
11248 a.resSeq = ap->resSeq;
11249 strncpy(a.resName, ap->resName, 4);
11250 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
11252 MoleculeCalculatePiAnchorPosition(mol, idx);
11253 aref = AtomRefNew(mol, idx);
11254 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
11257 #pragma mark ------ Molecular Properties ------
11261 * set_property(name, value[, index]) -> value
11262 * set_property(name, values, group) -> values
11264 * Set molecular property. A property is a floating-point number with a specified name,
11265 * and can be set for each frame separately. The name of the property is given as a String.
11266 * The value can be a single floating point number, which is set to the current frame.
11270 s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
11273 VALUE nval, vval, ival;
11276 Int i, n, idx, fidx;
11278 rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
11279 Data_Get_Struct(self, Molecule, mol);
11280 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11281 idx = NUM2INT(rb_Integer(nval));
11282 if (idx < 0 || idx >= mol->nmolprops)
11283 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11285 name = StringValuePtr(nval);
11286 idx = MoleculeLookUpProperty(mol, name);
11288 idx = MoleculeCreateProperty(mol, name);
11290 rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
11293 if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
11295 fidx = mol->cframe;
11297 fidx = NUM2INT(rb_Integer(ival));
11298 n = MoleculeGetNumberOfFrames(mol);
11299 if (fidx < 0 || fidx >= n)
11300 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11302 ig = IntGroupNewWithPoints(fidx, 1, -1);
11303 dp = (Double *)malloc(sizeof(Double));
11304 *dp = NUM2DBL(rb_Float(vval));
11307 vval = rb_ary_to_ary(vval);
11308 ig = IntGroupFromValue(ival);
11309 n = IntGroupGetCount(ig);
11311 rb_raise(rb_eMolbyError, "No frames are specified");
11312 if (RARRAY_LEN(vval) < n)
11313 rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
11314 dp = (Double *)calloc(sizeof(Double), n);
11315 for (i = 0; i < n; i++)
11316 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
11319 MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
11321 IntGroupRelease(ig);
11327 * get_property(name[, index]) -> value
11328 * get_property(name, group) -> values
11330 * Get molecular property. In the first form, a property value for a single frame is returned.
11331 * (If index is omitted, then the value for the current frame is given)
11332 * In the second form, an array of property values for the given frames is returned.
11333 * If name is not one of known properties or a valid index integer, exception is raised.
11336 s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
11342 Int i, n, idx, fidx;
11344 rb_scan_args(argc, argv, "11", &nval, &ival);
11345 Data_Get_Struct(self, Molecule, mol);
11346 if (mol->nmolprops == 0)
11347 rb_raise(rb_eMolbyError, "The molecule has no properties");
11348 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
11349 idx = NUM2INT(rb_Integer(nval));
11350 if (idx < 0 || idx >= mol->nmolprops)
11351 rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
11353 name = StringValuePtr(nval);
11354 idx = MoleculeLookUpProperty(mol, name);
11356 rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
11358 if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
11360 fidx = mol->cframe;
11362 fidx = NUM2INT(rb_Integer(ival));
11363 n = MoleculeGetNumberOfFrames(mol);
11364 if (fidx < 0 || fidx >= n)
11365 rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
11367 ig = IntGroupNewWithPoints(fidx, 1, -1);
11368 ival = INT2FIX(fidx);
11371 ig = IntGroupFromValue(ival);
11372 n = IntGroupGetCount(ig);
11374 return rb_ary_new();
11376 dp = (Double *)calloc(sizeof(Double), n);
11377 MoleculeGetProperty(mol, idx, ig, dp);
11378 if (FIXNUM_P(ival))
11379 ival = rb_float_new(dp[0]);
11381 ival = rb_ary_new();
11382 for (i = n - 1; i >= 0; i--) {
11383 nval = rb_float_new(dp[i]);
11384 rb_ary_store(ival, i, nval);
11388 IntGroupRelease(ig);
11394 * property_names -> Array
11396 * Get an array of property names.
11399 s_Molecule_PropertyNames(VALUE self)
11404 Data_Get_Struct(self, Molecule, mol);
11405 rval = rb_ary_new();
11406 for (i = mol->nmolprops - 1; i >= 0; i--) {
11407 nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
11408 rb_ary_store(rval, i, nval);
11413 #pragma mark ------ Class methods ------
11417 * current -> Molecule
11419 * Get the currently "active" molecule.
11422 s_Molecule_Current(VALUE klass)
11424 return ValueFromMolecule(MoleculeCallback_currentMolecule());
11429 * Molecule[] -> Molecule
11430 * Molecule[n] -> Molecule
11431 * Molecule[name] -> Molecule
11432 * Molecule[name, k] -> Molecule
11433 * Molecule[regex] -> Molecule
11434 * Molecule[regex, k] -> Molecule
11436 * Molecule[] is equivalent to Molecule.current.
11437 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
11438 * Molecule[name] gives the first document (in the order of creation time) that has
11439 * the given name. If a second argument (k) is given, the k-th document that has the
11440 * given name is returned.
11441 * Molecule[regex] gives the first document (in the order of creation time) that
11442 * has a name matching the regular expression. If a second argument (k) is given,
11443 * the k-th document that has a name matching the re is returned.
11446 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
11452 rb_scan_args(argc, argv, "02", &val, &kval);
11454 return s_Molecule_Current(klass);
11455 if (rb_obj_is_kind_of(val, rb_cInteger)) {
11456 idx = NUM2INT(val);
11457 mol = MoleculeCallback_moleculeAtIndex(idx);
11458 } else if (rb_obj_is_kind_of(val, rb_cString)) {
11459 char *p = StringValuePtr(val);
11460 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11461 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11462 MoleculeCallback_displayName(mol, buf, sizeof buf);
11463 if (strcmp(buf, p) == 0 && --k == 0)
11466 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
11467 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
11468 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
11470 MoleculeCallback_displayName(mol, buf, sizeof buf);
11471 name = Ruby_NewEncodedStringValue2(buf);
11472 if (rb_reg_match(val, name) != Qnil && --k == 0)
11475 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
11479 else return ValueFromMolecule(mol);
11484 * list -> array of Molecules
11486 * Get the list of molecules associated to the documents, in the order of creation
11487 * time of the document. If no document is open, returns an empry array.
11490 s_Molecule_List(VALUE klass)
11496 ary = rb_ary_new();
11497 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
11498 rb_ary_push(ary, ValueFromMolecule(mol));
11506 * ordered_list -> array of Molecules
11508 * Get the list of molecules associated to the documents, in the order of front-to-back
11509 * ordering of the associated window. If no document is open, returns an empry array.
11512 s_Molecule_OrderedList(VALUE klass)
11518 ary = rb_ary_new();
11519 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
11520 rb_ary_push(ary, ValueFromMolecule(mol));
11526 #pragma mark ------ Call Subprocess ------
11528 /* The callback functions for call_subprocess_async */
11530 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
11533 VALUE procval, retval, args[2];
11534 args[0] = ValueFromMolecule(mol);
11535 args[1] = INT2NUM(status);
11536 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
11537 if (procval != Qnil) {
11538 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11539 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11546 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
11549 VALUE procval, retval, args[2];
11550 args[0] = ValueFromMolecule(mol);
11551 args[1] = INT2NUM(tcount);
11552 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
11553 if (procval != Qnil) {
11554 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
11555 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
11563 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
11565 * Call subprocess asynchronically.
11566 * cmd is either a single string of an array of string. If it is a single string, then
11567 * it will be given to wxExecute as a single argument. In this case, the string can be
11568 * split into arguments by whitespace. If this behavior is not intended, then use an array
11569 * containing a single string.
11570 * If end_callback is given, it will be called (with two arguments self and termination status)
11571 * when the subprocess terminated.
11572 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
11573 * If timer_callback returns nil or false, then the subprocess will be interrupted.
11574 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
11575 * filename begins with ">>", then the message will be appended to the file.
11576 * If the filename is "/dev/null" or "NUL", then the message will be lost.
11577 * If the argument is nil, then the message will be sent to the Ruby console.
11578 * Returns the process ID as an integer.
11581 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
11583 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
11586 const char **cmdargv;
11588 FILE *fpout, *fperr;
11589 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
11590 Data_Get_Struct(self, Molecule, mol);
11592 if (stdout_val == Qnil) {
11595 sout = StringValuePtr(stdout_val);
11596 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
11599 if (strncmp(sout, ">>", 2) == 0) {
11601 fpout = fopen(sout, "a");
11605 fpout = fopen(sout, "w");
11608 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
11611 if (stderr_val == Qnil) {
11614 serr = StringValuePtr(stderr_val);
11615 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
11618 if (strncmp(serr, ">>", 2) == 0) {
11620 fpout = fopen(serr, "a");
11624 fperr = fopen(serr, "w");
11627 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
11631 /* Register procs as instance variables */
11632 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
11633 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
11635 if (rb_obj_is_kind_of(cmd, rb_cString)) {
11636 cmdargv = calloc(sizeof(cmdargv[0]), 3);
11637 cmdargv[0] = StringValuePtr(cmd);
11641 cmd = rb_ary_to_ary(cmd);
11642 cmdargv = calloc(sizeof(cmdargv[0]), RARRAY_LEN(cmd) + 1);
11643 for (n = 0; n < RARRAY_LEN(cmd); n++) {
11644 cmdargv[n] = StringValuePtr(RARRAY_PTR(cmd)[n]);
11648 n = MoleculeCallback_callSubProcessAsync(mol, cmdargv, s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
11649 if (fpout != NULL && fpout != (FILE *)1)
11651 if (fperr != NULL && fperr != (FILE *)1)
11656 #pragma mark ====== Define Molby Classes ======
11663 /* Define module Molby */
11664 rb_mMolby = rb_define_module("Molby");
11666 /* Define Vector3D, Transform, IntGroup */
11669 /* Define MDArena */
11670 Init_MolbyMDTypes();
11672 /* class Molecule */
11673 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
11675 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
11676 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
11677 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
11678 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
11679 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
11681 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
11682 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
11683 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
11684 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
11685 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
11686 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
11687 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
11688 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
11689 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
11690 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
11691 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
11692 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
11693 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
11694 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
11695 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
11696 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
11697 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
11698 rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
11699 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
11700 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
11701 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
11702 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
11704 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
11705 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
11706 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
11707 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
11708 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
11710 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
11711 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
11712 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
11713 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
11714 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
11715 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
11716 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
11717 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
11718 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
11719 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
11720 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
11721 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
11722 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
11723 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
11724 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
11726 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
11727 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
11728 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
11729 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
11730 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
11732 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
11733 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
11734 rb_define_alias(rb_cMolecule, "+", "add");
11735 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
11736 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
11737 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
11738 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
11739 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
11740 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
11741 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
11742 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
11743 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
11744 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
11745 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
11746 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
11747 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
11748 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
11749 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
11750 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
11751 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
11752 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
11753 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
11754 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
11755 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
11757 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
11758 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
11759 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
11760 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
11761 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
11763 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
11764 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
11765 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
11766 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
11767 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
11768 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
11769 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
11770 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
11771 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
11773 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
11774 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
11775 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
11776 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
11777 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
11778 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
11779 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
11780 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
11781 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
11782 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
11783 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
11784 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
11785 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
11786 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
11787 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
11788 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
11789 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
11790 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
11791 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
11792 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
11793 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
11794 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
11796 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
11797 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
11798 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
11799 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
11800 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
11801 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
11802 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
11804 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
11805 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
11806 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
11807 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
11808 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
11809 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
11810 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
11811 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
11812 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
11813 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
11814 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
11815 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
11816 rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
11818 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
11819 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
11820 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
11821 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
11822 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
11823 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
11825 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
11826 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
11827 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
11828 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
11829 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
11830 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
11831 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
11832 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
11833 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
11834 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
11835 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
11836 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
11837 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
11838 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
11839 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
11840 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
11841 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
11842 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
11843 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
11844 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
11845 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
11846 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
11847 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
11848 rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
11849 rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
11850 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
11851 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
11852 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
11853 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
11854 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
11855 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
11856 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
11857 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
11858 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
11859 rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
11860 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
11861 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
11862 rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
11863 rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
11864 rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
11865 rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
11866 rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
11867 rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
11868 rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
11869 rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
11870 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
11871 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
11872 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
11873 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
11874 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
11875 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
11876 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
11877 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
11878 rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
11880 rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
11881 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
11882 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
11883 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
11884 rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
11885 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
11886 rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
11887 rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
11888 rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
11889 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
11890 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
11891 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
11892 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
11894 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
11895 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
11896 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
11897 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
11898 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
11899 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
11900 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
11901 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
11902 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
11903 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11904 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11905 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11906 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11907 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
11909 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
11910 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
11911 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
11912 rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
11913 rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
11914 rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
11915 rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
11916 rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
11917 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
11918 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
11919 rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
11920 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, -1);
11921 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
11922 rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
11923 rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
11924 rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
11925 rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
11926 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
11927 rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
11928 rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
11929 rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
11930 rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
11931 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
11933 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
11934 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
11936 rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
11937 rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
11938 rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
11940 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
11941 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
11942 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
11943 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
11945 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
11947 /* class MolEnumerable */
11948 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
11949 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
11950 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
11951 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
11952 rb_define_alias(rb_cMolEnumerable, "size", "length");
11953 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
11954 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
11956 /* class AtomRef */
11957 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
11958 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
11960 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
11961 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
11962 s_AtomAttrDefTable[i].id = rb_intern(buf);
11963 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
11965 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
11967 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
11968 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
11969 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
11970 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
11971 s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
11972 rb_global_variable(&s_SetAtomAttrString);
11973 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
11974 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
11976 /* class Parameter */
11977 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
11978 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
11979 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
11980 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
11981 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
11982 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
11983 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
11984 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
11985 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
11986 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
11987 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
11988 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
11989 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
11990 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
11991 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
11992 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
11993 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
11994 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
11995 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
11996 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
11997 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
11998 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
11999 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12000 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12001 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12002 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12003 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
12004 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
12005 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
12006 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
12007 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
12008 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
12009 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
12010 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
12011 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
12012 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
12013 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
12014 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
12015 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
12016 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
12017 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
12018 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
12019 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
12020 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
12021 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
12022 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
12023 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
12024 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
12025 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
12026 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
12027 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
12028 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
12029 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
12030 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
12032 /* class ParEnumerable */
12033 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
12034 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
12035 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
12036 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
12037 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
12038 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
12039 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
12040 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
12041 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
12042 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
12043 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
12045 /* class ParameterRef */
12046 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
12047 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
12049 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
12050 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
12051 s_ParameterAttrDefTable[i].id = rb_intern(buf);
12052 if (s_ParameterAttrDefTable[i].symref != NULL)
12053 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
12054 if (s_ParameterAttrDefTable[i].setter != NULL) {
12056 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
12059 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
12060 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
12061 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
12062 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
12063 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
12064 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
12065 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
12066 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
12068 /* class MolbyError */
12069 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
12071 /* module Kernel */
12072 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
12073 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
12074 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
12075 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
12076 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
12077 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
12078 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
12079 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
12080 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
12081 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
12082 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
12083 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
12084 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
12085 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
12086 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
12087 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
12088 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
12089 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
12090 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
12091 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
12092 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
12093 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
12094 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
12095 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
12096 rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
12097 rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
12098 rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
12099 rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
12100 rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
12101 rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
12104 rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
12106 s_ID_equal = rb_intern("==");
12107 g_RubyID_call = rb_intern("call");
12109 s_InitMOInfoKeys();
12111 /* Symbols for graphics */
12112 s_LineSym = ID2SYM(rb_intern("line"));
12113 s_PolySym = ID2SYM(rb_intern("poly"));
12114 s_CylinderSym = ID2SYM(rb_intern("cylinder"));
12115 s_ConeSym = ID2SYM(rb_intern("cone"));
12116 s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
12119 #pragma mark ====== Interface with RubyDialog class ======
12122 RubyDialogCallback_parentModule(void)
12124 return (RubyValue)rb_mMolby;
12127 #pragma mark ====== External functions ======
12129 static VALUE s_ruby_top_self = Qfalse;
12130 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
12131 static VALUE s_ruby_export_local_variables = Qfalse;
12134 s_evalRubyScriptOnMoleculeSub(VALUE val)
12136 void **ptr = (void **)val;
12137 Molecule *mol = (Molecule *)ptr[1];
12138 VALUE sval, fnval, lnval, retval;
12141 /* Clear the error information (store in the history array if necessary) */
12142 sval = rb_errinfo();
12143 if (sval != Qnil) {
12144 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
12145 rb_set_errinfo(Qnil);
12148 if (s_ruby_top_self == Qfalse) {
12149 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
12151 if (s_ruby_get_binding_for_molecule == Qfalse) {
12153 "lambda { |_mol_, _bind_| \n"
12154 " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
12155 " _proc_.call(_mol_) } ";
12156 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
12157 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
12159 if (s_ruby_export_local_variables == Qfalse) {
12161 "lambda { |_bind_| \n"
12162 " # find local variables newly defined in _bind_ \n"
12163 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
12164 " _a_.each { |_vsym_| \n"
12165 " _vname_ = _vsym_.to_s \n"
12166 " _vval_ = _bind_.eval(_vname_) \n"
12167 " # Define local variable \n"
12168 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
12169 " # Then set value \n"
12170 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
12173 s_ruby_export_local_variables = rb_eval_string(s2);
12174 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
12176 if (ptr[2] == NULL) {
12178 /* String literal: we need to specify string encoding */
12179 #if defined(__WXMSW__)
12180 asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
12182 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
12184 sval = Ruby_NewEncodedStringValue2(scr);
12186 fnval = Ruby_NewEncodedStringValue2("(eval)");
12187 lnval = INT2FIX(0);
12189 sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
12190 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
12191 lnval = INT2FIX(1);
12193 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
12195 VALUE mval = ValueFromMolecule(mol);
12196 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
12198 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
12200 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
12206 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
12210 VALUE save_interrupt_flag;
12211 /* char *save_ruby_sourcefile;
12212 int save_ruby_sourceline; */
12213 if (gMolbyIsCheckingInterrupt) {
12214 MolActionAlertRubyIsRunning();
12216 return (RubyValue)Qnil;
12219 args[0] = (void *)script;
12220 args[1] = (void *)mol;
12221 args[2] = (void *)fname;
12222 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
12223 /* save_ruby_sourcefile = ruby_sourcefile;
12224 save_ruby_sourceline = ruby_sourceline; */
12225 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
12226 if (*status != 0) {
12227 /* Is this 'exit' exception? */
12228 VALUE last_exception = rb_gv_get("$!");
12229 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
12230 /* Capture exit and return the status value */
12231 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
12233 rb_set_errinfo(Qnil);
12236 s_SetInterruptFlag(Qnil, save_interrupt_flag);
12237 /* ruby_sourcefile = save_ruby_sourcefile;
12238 ruby_sourceline = save_ruby_sourceline; */
12245 Ruby_inspectValue(RubyValue value)
12248 static char buf[256];
12249 VALUE val = (VALUE)value;
12251 val = rb_protect(rb_inspect, val, &status);
12254 char *str = StringValuePtr(val);
12255 strncpy(buf, str, sizeof(buf) - 1);
12256 buf[sizeof(buf) - 1] = 0;
12258 snprintf(buf, sizeof(buf), "Error status = %d", status);
12264 Ruby_showValue(RubyValue value, char **outValueString)
12266 VALUE val = (VALUE)value;
12267 if (gMolbyIsCheckingInterrupt) {
12268 MolActionAlertRubyIsRunning();
12275 val = rb_protect(rb_inspect, val, &status);
12279 str = StringValuePtr(val);
12280 if (outValueString != NULL)
12281 *outValueString = strdup(str);
12282 MyAppCallback_showScriptMessage("%s", str);
12284 if (outValueString != NULL)
12285 *outValueString = NULL;
12291 Ruby_showError(int status)
12293 static const int tag_raise = 6;
12294 char *main_message = "Molby script error";
12295 char *msg = NULL, *msg2;
12296 VALUE val, backtrace;
12297 int interrupted = 0;
12298 int exit_status = -1;
12299 if (status == tag_raise) {
12300 VALUE errinfo = rb_errinfo();
12301 VALUE eclass = CLASS_OF(errinfo);
12302 if (eclass == rb_eInterrupt) {
12303 main_message = "Molby script interrupted";
12306 } else if (eclass == rb_eSystemExit) {
12307 main_message = "Molby script exit";
12309 val = rb_eval_string_protect("$!.status", &status);
12311 exit_status = NUM2INT(rb_Integer(val));
12312 asprintf(&msg, "Molby script exit with status %d", exit_status);
12314 asprintf(&msg, "Molby script exit with unknown status");
12319 if (exit_status != 0) {
12320 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
12322 val = rb_eval_string_protect("$!.to_s", &status);
12324 msg = RSTRING_PTR(val);
12326 msg = "(message not available)";
12328 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
12330 msg2 = strdup(msg);
12332 MyAppCallback_messageBox(msg2, main_message, 0, 3);
12334 if (interrupted == 2) {
12336 if (!gUseGUI && exit_status == 0)
12337 exit(0); // Capture exit(0) here and force exit
12342 /* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
12344 Molby_loadScript(const char *script, int from_file)
12349 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12351 rb_eval_string_protect(script, &status);
12357 Molby_getDescription(char **versionString, char **auxString)
12359 extern const char *gVersionString, *gCopyrightString;
12360 extern int gRevisionNumber;
12361 extern char *gLastBuildString;
12363 char *revisionString;
12364 if (gRevisionNumber > 0) {
12365 asprintf(&revisionString, ", revision %d", gRevisionNumber);
12366 } else revisionString = "";
12368 asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
12369 #if defined(__WXMSW__)
12370 #if TARGET_ARCH == 64
12378 gVersionString, revisionString, gCopyrightString, gLastBuildString);
12383 "ruby %s, http://www.ruby-lang.org/\n"
12385 "FFTW 3.3.2, http://www.fftw.org/\n"
12386 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12387 " and Massachusetts Institute of Technology\n"
12388 "JANPA 2.01, https://janpa.sourceforge.net/\n"
12389 " Copyright (C) 2014, Tymofii Nikolaienko",
12390 MyAppCallback_getGUIDescriptionString(),
12391 gRubyVersion, gRubyCopyright);
12395 "ruby %s, http://www.ruby-lang.org/\n"
12397 "FFTW 3.3.2, http://www.fftw.org/\n"
12398 " Copyright (C) 2003, 2007-11 Matteo Frigo"
12399 " and Massachusetts Institute of Technology\n"
12400 "JANPA 2.01, https://janpa.sourceforge.net/\n"
12401 " Copyright (C) 2014, Tymofii Nikolaienko",
12402 gRubyVersion, gRubyCopyright);
12405 if (revisionString[0] != 0)
12406 free(revisionString);
12407 if (versionString != NULL)
12408 *versionString = s1;
12409 if (auxString != NULL)
12414 Molby_startup(const char *script, const char *dir)
12419 char *respath, *p, *wbuf;
12421 /* Get version/copyright string from Ruby interpreter */
12423 gRubyVersion = strdup(ruby_version);
12424 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
12425 " ", /* Indent for displaying in About dialog */
12426 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
12429 /* Read build and revision information for Molby */
12432 extern int gRevisionNumber;
12433 extern char *gLastBuildString;
12434 FILE *fp = fopen("../buildInfo.txt", "r");
12435 gLastBuildString = "";
12437 if (fgets(buf, sizeof(buf), fp) != NULL) {
12438 char *p1 = strchr(buf, '\"');
12439 char *p2 = strrchr(buf, '\"');
12440 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
12441 memmove(buf, p1 + 1, p2 - p1 - 1);
12442 buf[p2 - p1 - 1] = 0;
12443 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
12448 fp = fopen("../revisionInfo.txt", "r");
12449 gRevisionNumber = 0;
12451 if (fgets(buf, sizeof(buf), fp) != NULL) {
12452 gRevisionNumber = strtol(buf, NULL, 0);
12460 Molby_getDescription(&wbuf, &wbuf2);
12461 MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
12466 /* Read atom display parameters */
12467 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
12468 MyAppCallback_setConsoleColor(1);
12469 MyAppCallback_showScriptMessage("%s", wbuf);
12470 MyAppCallback_setConsoleColor(0);
12474 /* Read default parameters */
12475 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
12476 if (wbuf != NULL) {
12477 MyAppCallback_setConsoleColor(1);
12478 MyAppCallback_showScriptMessage("%s", wbuf);
12479 MyAppCallback_setConsoleColor(0);
12483 /* Initialize Ruby interpreter */
12486 /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
12487 it causes rb_bug() (= fatal error) during ruby_init().
12488 As a workaround, these standard streams are reopend as
12490 freopen("NUL", "r", stdin);
12491 freopen("NUL", "w", stdout);
12492 freopen("NUL", "w", stderr);
12498 /* Initialize CP932/Windows-31J encodings */
12499 extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
12500 extern int rb_enc_alias(const char *, const char *);
12502 Init_windows_31j();
12503 Init_trans_japanese_sjis();
12504 rb_enc_alias("CP932", "Windows-31J");
12507 #if defined(__WXMSW__)
12509 /* Set default external encoding */
12510 /* The following snippet is taken from encoding.c */
12511 extern void rb_enc_set_default_external(VALUE encoding);
12512 char cp[sizeof(int) * 8 / 3 + 22];
12515 snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
12516 enc = rb_eval_string_protect(cp, &status);
12517 if (status == 0 && !NIL_P(enc)) {
12518 rb_enc_set_default_external(enc);
12523 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
12525 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
12526 ruby_incpush(libpath);
12530 ruby_script("Molby");
12532 /* Find the resource path (the parent directory of the given directory) */
12533 respath = strdup(dir);
12534 p = strrchr(respath, '/');
12535 if (p == NULL && PATH_SEPARATOR != '/')
12536 p = strrchr(respath, PATH_SEPARATOR);
12539 val = Ruby_NewFileStringValue(respath);
12540 rb_define_global_const("MolbyResourcePath", val);
12543 /* Define Molby classes */
12546 RubyDialogInitClass();
12548 rb_define_const(rb_mMolby, "ResourcePath", val);
12549 val = Ruby_NewFileStringValue(dir);
12550 rb_define_const(rb_mMolby, "ScriptPath", val);
12551 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
12552 val = Ruby_NewFileStringValue(p);
12553 rb_define_const(rb_mMolby, "MbsfPath", val);
12556 p = MyAppCallback_getHomeDir();
12557 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12558 rb_define_const(rb_mMolby, "HomeDirectory", val);
12560 p = MyAppCallback_getDocumentHomeDir();
12561 val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
12562 rb_define_const(rb_mMolby, "DocumentDirectory", val);
12566 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
12568 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
12571 /* Create objects for stdout and stderr */
12572 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12573 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
12574 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12575 rb_gv_set("$stdout", val);
12576 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12577 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
12578 rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
12579 rb_gv_set("$stderr", val);
12581 /* Create objects for stdin */
12582 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
12583 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
12584 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
12585 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
12586 rb_gv_set("$stdin", val);
12589 /* Global variable to hold error information */
12590 rb_define_variable("$backtrace", &gMolbyBacktrace);
12591 rb_define_variable("$error_history", &gMolbyErrorHistory);
12592 gMolbyErrorHistory = rb_ary_new();
12594 /* Global variables for script menus */
12595 rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
12596 rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
12597 gScriptMenuCommands = rb_ary_new();
12598 gScriptMenuEnablers = rb_ary_new();
12601 /* Register interrupt check code */
12602 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
12603 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
12604 s_SetIntervalTimer(0, 50);
12607 /* Read the startup script */
12608 if (script != NULL && script[0] != 0) {
12609 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
12611 rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
12614 Ruby_showError(status);
12616 MyAppCallback_showScriptMessage("Done.\n");
12621 Molby_buildARGV(int argc, const char **argv)
12624 rb_ary_clear(rb_argv);
12625 for (i = 0; i < argc; i++) {
12626 VALUE arg = rb_tainted_str_new2(argv[i]);
12628 rb_ary_push(rb_argv, arg);