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 <node.h> *//* for rb_add_event_hook() */
32 #if defined(__WXMAC__) || defined(__CMDMAC__)
33 #define USE_PTHREAD_FOR_TIMER 1
37 #if USE_PTHREAD_FOR_TIMER
38 #include <unistd.h> /* for usleep() */
39 #include <pthread.h> /* for pthread */
41 #include <signal.h> /* for sigaction() */
45 #include "../Missing.h"
47 #pragma mark ====== Global Values ======
51 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
52 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
54 VALUE gMolbyBacktrace;
56 int gMolbyRunLevel = 0;
57 int gMolbyIsCheckingInterrupt = 0;
59 char *gRubyVersion, *gRubyCopyright;
62 static ID s_ID_equal; /* rb_intern("==") */
66 /* Symbols for atom attributes */
68 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
69 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
70 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
71 s_RSym, s_XSym, s_YSym, s_ZSym,
72 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
73 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
74 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
75 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
76 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
77 s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
79 /* Symbols for parameter attributes */
81 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
82 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
85 /* s_A14Sym, s_B14Sym, */
86 s_Req14Sym, s_Eps14Sym,
87 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
88 s_CommentSym, s_SourceSym;
92 * Get ary[i] by calling "[]" method
95 Ruby_ObjectAtIndex(VALUE ary, int idx)
97 static ID index_method = 0;
98 if (TYPE(ary) == T_ARRAY) {
99 int len = RARRAY_LEN(ary);
100 if (idx >= 0 && idx < len)
101 return (RARRAY_PTR(ary))[idx];
104 if (index_method == 0)
105 index_method = rb_intern("[]");
106 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
110 Ruby_FileStringValuePtr(VALUE *valp)
113 char *p = strdup(StringValuePtr(*valp));
114 translate_char(p, '/', '\\');
115 *valp = rb_str_new2(p);
117 return StringValuePtr(*valp);
119 return StringValuePtr(*valp);
124 Ruby_NewFileStringValue(const char *fstr)
128 char *p = strdup(fstr);
129 translate_char(p, '\\', '/');
130 retval = rb_str_new2(p);
134 return rb_str_new2(fstr);
139 Ruby_ObjToStringObj(VALUE val)
145 return rb_str_new2(rb_id2name(SYM2ID(val)));
147 return rb_str_to_str(val);
151 #pragma mark ====== Message input/output ======
155 * message_box(str, title, button = nil, icon = :info)
157 * Show a message box.
158 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
159 * Icon: :info, :warning, :error
162 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
164 char *str, *title, *s;
166 VALUE sval, tval, bval, ival;
167 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
168 str = StringValuePtr(sval);
169 title = StringValuePtr(tval);
171 bval = Ruby_ObjToStringObj(bval);
172 s = RSTRING_PTR(bval);
173 if (strncmp(s, "ok", 2) == 0)
175 else if (strncmp(s, "cancel", 6) == 0)
178 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
181 ival = Ruby_ObjToStringObj(ival);
182 s = RSTRING_PTR(ival);
183 if (strncmp(s, "info", 4) == 0)
185 else if (strncmp(s, "warn", 4) == 0)
187 else if (strncmp(s, "err", 3) == 0)
190 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
192 MyAppCallback_messageBox(str, title, buttons, icon);
198 * error_message_box(str)
200 * Show an error message box.
203 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
205 char *str = StringValuePtr(sval);
206 MyAppCallback_errorMessageBox("%s", str);
212 * ask(prompt, default = nil) -> string
214 * Open a modal dialog and get a line of text.
217 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
219 volatile VALUE prompt, message;
222 rb_scan_args(argc, argv, "11", &prompt, &message);
223 if (message != Qnil) {
224 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
225 buf[sizeof buf - 1] = 0;
227 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
229 return rb_str_new2(buf);
236 * show_console_window
238 * Show the console window and bring to the front.
241 s_Kernel_ShowConsoleWindow(VALUE self)
243 MyAppCallback_showConsoleWindow();
249 * hide_console_window
251 * Hide the console window.
254 s_Kernel_HideConsoleWindow(VALUE self)
256 MyAppCallback_hideConsoleWindow();
264 * Ring the system bell.
267 s_Kernel_Bell(VALUE self)
269 MyAppCallback_bell();
275 * play_sound(filename, flag = 0)
277 * Play the sound (a WAV file) in the file. Flag: 0, pause until sound ends;
278 * 1, play the sound asynchronously; 3, play the sound with loop asynchronously
281 s_Kernel_PlaySound(int argc, VALUE *argv, VALUE self)
286 rb_scan_args(argc, argv, "11", &fnval, &flval);
289 else flag = NUM2INT(rb_Integer(flval));
290 fnval = rb_funcall(rb_cFile, rb_intern("expand_path"), 1, fnval);
291 fname = StringValuePtr(fnval);
292 retval = MyAppCallback_playSound(fname, flag);
293 return (retval ? Qtrue : Qnil);
300 * Stop the sound if playing.
303 s_Kernel_StopSound(VALUE self)
305 MyAppCallback_stopSound();
311 * export_to_clipboard(str)
313 * Export the given string to clipboard.
316 s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
318 #if !defined(__CMDMAC__)
319 const char *s = StringValuePtr(sval);
322 /* Convert the end-of-line characters */
323 { const char *p; int nc; char *np;
325 for (p = s; *p != 0; p++) {
329 ns = (char *)malloc(strlen(s) + nc + 1);
330 for (np = ns, p = s; *p != 0; p++, np++) {
338 ns = (char *)malloc(strlen(s) + 1);
342 /* wxMac still has Carbon code. Oops. */
343 for (np = ns; *np != 0; np++) {
350 if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
351 rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
360 * Put the message in the main text view in black color.
363 s_StandardOutput(VALUE self, VALUE str)
366 MyAppCallback_setConsoleColor(0);
367 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
375 * Put the message in the main text view in red color.
378 s_StandardErrorOutput(VALUE self, VALUE str)
381 MyAppCallback_setConsoleColor(1);
382 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
383 MyAppCallback_setConsoleColor(0);
389 * stdin.gets(rs = $/)
391 * Read one line message via dialog box.
394 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
397 pval = rb_str_new2("Enter a line:");
398 rval = s_Kernel_Ask(1, &pval, self);
401 rb_str_cat2(rval, "\n");
407 * stdin.method_missing(name, args, ...)
409 * Throw an exception, noting only gets and readline are defined.
412 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
415 rb_scan_args(argc, argv, "10", &nval);
416 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
417 return Qnil; /* Not reached */
420 #pragma mark ====== Track key events ======
422 /* User interrupt handling
423 * User interrupt (command-period on Mac OS) is handled by periodic polling of
424 * key events. This polling should only be enabled during "normal" execution
425 * of scripts and must be disabled when the rest of the application (or Ruby
426 * script itself) is handling GUI. This is ensured by appropriate calls to
427 * enable_interrupt and disable_interrupt. */
429 static VALUE s_interrupt_flag = Qfalse;
432 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
434 volatile VALUE message;
436 if (Ruby_GetInterruptFlag() == Qtrue) {
437 rb_scan_args(argc, argv, "01", &message);
439 p = StringValuePtr(message);
442 MyAppCallback_showProgressPanel(p);
448 s_HideProgressPanel(VALUE self)
450 MyAppCallback_hideProgressPanel();
455 s_SetProgressValue(VALUE self, VALUE val)
457 double dval = NUM2DBL(rb_Float(val));
458 MyAppCallback_setProgressValue(dval);
463 s_SetProgressMessage(VALUE self, VALUE msg)
468 else p = StringValuePtr(msg);
469 MyAppCallback_setProgressMessage(p);
474 s_SetInterruptFlag(VALUE self, VALUE val)
478 if (val == Qfalse || val == Qnil)
482 oldval = s_interrupt_flag;
484 s_interrupt_flag = val;
486 s_HideProgressPanel(self);
493 s_GetInterruptFlag(VALUE self)
495 return s_SetInterruptFlag(self, Qundef);
500 s_Ruby_CallMethod(VALUE val)
502 void **ptr = (void **)val;
503 VALUE receiver = (VALUE)ptr[0];
504 ID method_id = (ID)ptr[1];
505 VALUE args = (VALUE)ptr[2];
507 if (method_id == 0) {
508 /* args should be a string, which is evaluated */
509 if (receiver == Qnil) {
510 retval = rb_eval_string(StringValuePtr(args));
512 retval = rb_obj_instance_eval(1, &args, receiver);
515 /* args should be an array of arguments */
516 retval = rb_apply(receiver, method_id, args);
522 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
524 VALUE retval, save_interrupt_flag;
526 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
527 ptr[0] = (void *)receiver;
528 ptr[1] = (void *)method_id;
529 ptr[2] = (void *)args;
530 MyAppCallback_beginUndoGrouping();
531 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
532 MyAppCallback_endUndoGrouping();
533 s_SetInterruptFlag(Qnil, save_interrupt_flag);
534 MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */
\v
540 Ruby_SetInterruptFlag(VALUE val)
542 return s_SetInterruptFlag(Qnil, val);
546 Ruby_GetInterruptFlag(void)
548 return s_SetInterruptFlag(Qnil, Qundef);
553 * check_interrupt -> integer
555 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
558 s_Kernel_CheckInterrupt(VALUE self)
560 if (Ruby_GetInterruptFlag() == Qfalse)
562 else if (MyAppCallback_checkInterrupt())
564 else return INT2NUM(0);
567 static volatile unsigned long sITimerCount = 0;
570 static HANDLE sITimerEvent;
571 static HANDLE sITimerThread;
572 static int sITimerInterval;
574 static __stdcall unsigned
575 s_ITimerThreadFunc(void *p)
577 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
583 #elif USE_PTHREAD_FOR_TIMER
586 static pthread_t sTimerThread;
588 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
589 static volatile signed char sTimerFlag = -1;
590 static volatile int sTimerIntervalMicrosec = 0;
593 s_TimerThreadEntry(void *param)
596 usleep(sTimerIntervalMicrosec);
599 else if (sTimerFlag == -2)
608 s_SignalAction(int n)
614 s_SetIntervalTimer(int n, int msec)
618 /* Start interval timer */
619 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
620 sITimerInterval = msec;
622 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
625 /* Stop interval timer */
627 SetEvent(sITimerEvent); /* Tell thread to terminate */
629 WaitForSingleObject(sITimerThread, 1000);
630 CloseHandle(sITimerThread);
633 CloseHandle(sITimerEvent);
635 sITimerThread = NULL;
637 #elif USE_PTHREAD_FOR_TIMER
639 if (sTimerFlag == -1) {
640 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
642 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
645 sTimerFlag = 0; /* Active */
646 sTimerIntervalMicrosec = msec * 1000;
647 } else if (sTimerFlag != -1)
648 sTimerFlag = 1; /* Inactive */
650 static struct itimerval sOldValue;
651 static struct sigaction sOldAction;
652 struct itimerval val;
653 struct sigaction act;
656 act.sa_handler = s_SignalAction;
659 sigaction(SIGALRM, &act, &sOldAction);
660 val.it_value.tv_sec = 0;
661 val.it_value.tv_usec = msec * 1000;
662 val.it_interval.tv_sec = 0;
663 val.it_interval.tv_usec = msec * 1000;
664 setitimer(ITIMER_REAL, &val, &sOldValue);
666 setitimer(ITIMER_REAL, &sOldValue, &val);
667 sigaction(SIGALRM, &sOldAction, &act);
673 s_GetTimerCount(void)
679 //s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
680 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
682 if (s_interrupt_flag != Qfalse) {
683 static unsigned long sLastTime = 0;
684 unsigned long currentTime;
686 currentTime = s_GetTimerCount();
687 if (currentTime != sLastTime) {
688 sLastTime = currentTime;
689 gMolbyIsCheckingInterrupt = 1;
690 flag = MyAppCallback_checkInterrupt();
691 gMolbyIsCheckingInterrupt = 0;
693 s_SetInterruptFlag(Qnil, Qfalse);
700 #pragma mark ====== Menu handling ======
704 * register_menu(title, method)
706 * Register the method (specified as a symbol) in the script menu.
707 * The method must be either an instance method of Molecule with no argument,
708 * or a class method of Molecule with one argument (the current molecule).
709 * The menu associated with the class method can be invoked even when no document
710 * is open (the argument is set to Qnil in this case). On the other hand, the
711 * menu associated with the instance method can only be invoked when at least one
712 * document is active.
715 s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
717 if (TYPE(method) == T_SYMBOL) {
718 method = rb_funcall(method, rb_intern("to_s"), 0);
720 MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
725 s_Kernel_LookupMenu(VALUE self, VALUE title)
727 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
732 s_Ruby_methodType_sub(VALUE data)
734 const char **p = (const char **)data;
735 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
736 ID mid = rb_intern(p[1]);
738 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
740 else if (rb_respond_to(klass, mid))
743 return INT2FIX(ival);
746 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
747 has the singleton method (class method) with the given name, 0 otherwise. */
749 Ruby_methodType(const char *className, const char *methodName)
756 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
758 return FIX2INT(retval);
764 * execute_script_file(fname)
766 * Execute the script in the given file. If a molecule is active, then
767 * the script is evaluated as Molecule.current.instance_eval(script).
768 * Before entering the script, the current directory is set to the parent
769 * directory of the script.
772 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
775 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
776 if (retval == (VALUE)6 && status == -1)
777 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
787 * Get the directory suitable for storing user documents. On Windows
788 * it is the home directory + "My Documents". On other platforms
789 * it is the home directory.
792 s_Kernel_DocumentHome(VALUE self)
794 char *s = MyAppCallback_getDocumentHomeDir();
795 VALUE retval = Ruby_NewFileStringValue(s);
800 /* The callback function for call_subprocess */
802 s_Kernel_CallSubProcess_Callback(void *data)
805 VALUE retval = Ruby_funcall2_protect((VALUE)data, rb_intern("call"), 0, NULL, &status);
806 if (status != 0 || retval == Qnil || retval == Qfalse)
813 * call_subprocess(cmd, process_name [, callback_proc [, stdout_file [, stderr_file]]])
815 * Call subprocess. A progress dialog window is displayed, with a message
816 * "Running #{process_name}...".
817 * A callback proc can be given, which is called periodically during execution. If the proc returns
818 * nil or false, then the execution will be interrupted.
819 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
820 * filename begins with ">>", then the message will be appended to the file.
821 * If the filename is "/dev/null" or "NUL", then the message will be lost.
822 * If the argument is nil, then the message will be sent to the Ruby console.
825 s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
827 VALUE cmd, procname, cproc, stdout_val, stderr_val;
832 rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
834 if (stdout_val == Qnil) {
837 sout = StringValuePtr(stdout_val);
838 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
841 if (strncmp(sout, ">>", 2) == 0) {
843 fpout = fopen(sout, "a");
847 fpout = fopen(sout, "w");
850 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
853 if (stderr_val == Qnil) {
856 serr = StringValuePtr(stderr_val);
857 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
860 if (strncmp(serr, ">>", 2) == 0) {
862 fpout = fopen(serr, "a");
866 fperr = fopen(serr, "w");
869 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
873 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
875 if (fpout != NULL && fpout != (FILE *)1)
877 if (fperr != NULL && fperr != (FILE *)1)
889 * Same as the builtin backquote, except that, under Windows, no console window gets opened.
892 s_Kernel_Backquote(VALUE self, VALUE cmd)
897 n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
899 rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
901 val = rb_str_new2(buf);
904 val = rb_str_new2("");
909 #pragma mark ====== User defaults ======
913 * get_global_settings(key)
915 * Get a setting data for key from the application preferences.
918 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
920 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
922 VALUE retval = rb_eval_string(p);
930 * set_global_settings(key, value)
932 * Set a setting data for key to the application preferences.
935 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
937 VALUE sval = rb_inspect(value);
938 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
942 #pragma mark ====== Utility functions (protected funcall) ======
944 struct Ruby_funcall2_record {
952 s_Ruby_funcall2_sub(VALUE data)
954 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
955 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
959 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
961 struct Ruby_funcall2_record rec;
966 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
970 Ruby_funcall2_protect_extern(RubyValue recv, int mid, int argc, RubyValue *argv, int *status)
972 return (RubyValue)Ruby_funcall2_protect((VALUE)recv, mid, argc, (VALUE *)argv, status);
975 #pragma mark ====== ParameterRef Class ======
978 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
982 Data_Get_Struct(self, ParameterRef, pref);
984 *typep = pref->parType;
985 if (pref->parType == kElementParType) {
986 up = (UnionPar *)&gElementParameters[pref->idx];
988 up = ParameterRefGetPar(pref);
991 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
992 if (up->bond.src != 0 && up->bond.src != -1)
993 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
1000 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
1004 Data_Get_Struct(self, ParameterRef, pref);
1005 if (pref->mol == NULL)
1007 up = ParameterRefGetPar(pref);
1008 if (key != s_SourceSym)
1009 up->bond.src = 0; /* Becomes automatically molecule-local */
1010 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
1013 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
1014 MolActionCallback_registerUndo(pref->mol, act);
1015 MoleculeCallback_notifyModification(pref->mol, 0);
1016 pref->mol->needsMDRebuild = 1;
1021 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
1023 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
1025 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
1027 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
1031 s_AtomTypeIndexFromValue(VALUE val)
1033 if (rb_obj_is_kind_of(val, rb_cNumeric))
1034 return NUM2INT(val);
1036 return AtomTypeEncodeToUInt(StringValuePtr(val));
1039 static const char *s_ParameterTypeNames[] = {
1040 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
1042 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1045 s_ParTypeFromValue(VALUE val)
1049 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
1050 if (s_ParameterTypeIDs[0] == 0) {
1051 for (i = 0; i < n; i++)
1052 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
1054 valid = rb_to_id(val);
1055 for (i = 0; i < n; i++) {
1056 if (valid == s_ParameterTypeIDs[i]) {
1058 return kElementParType;
1059 else return kFirstParType + i;
1062 return kInvalidParType;
1069 * Get the index in the parameter list.
1071 static VALUE s_ParameterRef_GetIndex(VALUE self) {
1073 Data_Get_Struct(self, ParameterRef, pref);
1074 return INT2NUM(pref->idx);
1079 * par_type -> String
1081 * Get the parameter type, like "bond", "angle", etc.
1083 static VALUE s_ParameterRef_GetParType(VALUE self) {
1085 s_UnionParFromValue(self, &tp, 0);
1086 if (tp == kElementParType)
1087 return rb_str_new2("element");
1088 tp -= kFirstParType;
1089 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
1090 return rb_str_new2(s_ParameterTypeNames[tp]);
1091 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
1096 * atom_type -> String or Array of String
1097 * atom_types -> String or Array of String
1099 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
1100 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
1101 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
1102 * The atom type may be "X", which is a wildcard that matches any atom type.
1104 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
1109 up = s_UnionParFromValue(self, &tp, 0);
1110 n = ParameterGetAtomTypes(tp, up, types);
1112 rb_raise(rb_eMolbyError, "invalid member atom_types");
1113 for (i = 0; i < n; i++) {
1114 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
1115 vals[i] = INT2NUM(types[i]);
1117 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
1122 return rb_ary_new4(n, vals);
1129 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
1131 static VALUE s_ParameterRef_GetK(VALUE self) {
1135 up = s_UnionParFromValue(self, &tp, 0);
1138 return rb_float_new(up->bond.k * INTERNAL2KCAL);
1140 return rb_float_new(up->angle.k * INTERNAL2KCAL);
1141 case kDihedralParType:
1142 case kImproperParType:
1143 if (up->torsion.mult == 1)
1144 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
1145 n = up->torsion.mult;
1148 for (i = 0; i < n; i++)
1149 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
1150 return rb_ary_new4(n, vals);
1152 rb_raise(rb_eMolbyError, "invalid member k");
1160 * Get the equilibrium bond length. Only available for bond parameters.
1162 static VALUE s_ParameterRef_GetR0(VALUE self) {
1165 up = s_UnionParFromValue(self, &tp, 0);
1166 if (tp == kBondParType)
1167 return rb_float_new(up->bond.r0);
1168 else rb_raise(rb_eMolbyError, "invalid member r0");
1175 * Get the equilibrium angle (in degree). Only available for angle parameters.
1177 static VALUE s_ParameterRef_GetA0(VALUE self) {
1180 up = s_UnionParFromValue(self, &tp, 0);
1181 if (tp == kAngleParType)
1182 return rb_float_new(up->angle.a0 * kRad2Deg);
1183 else rb_raise(rb_eMolbyError, "invalid member a0");
1190 * Get the multiplicity. Only available for dihedral and improper parameters.
1191 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1193 static VALUE s_ParameterRef_GetMult(VALUE self) {
1196 up = s_UnionParFromValue(self, &tp, 0);
1197 if (tp == kDihedralParType || tp == kImproperParType)
1198 return rb_float_new(up->torsion.mult);
1199 else rb_raise(rb_eMolbyError, "invalid member mult");
1204 * period -> Integer or Array of Integers
1206 * Get the periodicity. Only available for dihedral and improper parameters.
1207 * If the multiplicity is larger than 1, then an array of integers is returned.
1208 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1210 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1214 up = s_UnionParFromValue(self, &tp, 0);
1215 if (tp == kDihedralParType || tp == kImproperParType) {
1216 if (up->torsion.mult == 1)
1217 return INT2NUM(up->torsion.period[0]);
1218 n = up->torsion.mult;
1221 for (i = 0; i < n; i++)
1222 vals[i] = INT2NUM(up->torsion.period[i]);
1223 return rb_ary_new4(n, vals);
1224 } else rb_raise(rb_eMolbyError, "invalid member period");
1229 * phi0 -> Float or Array of Floats
1231 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1232 * If the multiplicity is larger than 1, then an array of floats is returned.
1233 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1235 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1239 up = s_UnionParFromValue(self, &tp, 0);
1240 if (tp == kDihedralParType || tp == kImproperParType) {
1241 if (up->torsion.mult == 1)
1242 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1243 n = up->torsion.mult;
1246 for (i = 0; i < n; i++)
1247 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1248 return rb_ary_new4(n, vals);
1249 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1256 * Get the "A" value for the van der Waals parameter.
1259 static VALUE s_ParameterRef_GetA(VALUE self) {
1262 up = s_UnionParFromValue(self, &tp, 0);
1263 if (tp == kVdwParType)
1264 return rb_float_new(up->vdw.A);
1265 else if (tp == kVdwPairParType)
1266 return rb_float_new(up->vdwp.A);
1267 else rb_raise(rb_eMolbyError, "invalid member A");
1275 * Get the "B" value for the van der Waals parameter.
1278 static VALUE s_ParameterRef_GetB(VALUE self) {
1281 up = s_UnionParFromValue(self, &tp, 0);
1282 if (tp == kVdwParType)
1283 return rb_float_new(up->vdw.B);
1284 else if (tp == kVdwPairParType)
1285 return rb_float_new(up->vdwp.B);
1286 else rb_raise(rb_eMolbyError, "invalid member B");
1294 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1296 static VALUE s_ParameterRef_GetReq(VALUE self) {
1299 /* Double a, b, r; */
1301 up = s_UnionParFromValue(self, &tp, 0);
1302 if (tp == kVdwParType) {
1306 } else if (tp == kVdwPairParType) {
1310 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1311 /* if (a == 0.0 || b == 0.0) */
1312 return rb_float_new(r);
1313 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1320 * Get the minimum energy for the van der Waals parameter.
1322 static VALUE s_ParameterRef_GetEps(VALUE self) {
1327 up = s_UnionParFromValue(self, &tp, 0);
1328 if (tp == kVdwParType) {
1332 } else if (tp == kVdwPairParType) {
1336 } else rb_raise(rb_eMolbyError, "invalid member eps");
1337 /* if (a == 0.0 || b == 0.0) */
1338 return rb_float_new(eps * INTERNAL2KCAL);
1339 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1346 * Get the "A" value for the 1-4 van der Waals parameter.
1349 static VALUE s_ParameterRef_GetA14(VALUE self) {
1352 up = s_UnionParFromValue(self, &tp, 0);
1353 if (tp == kVdwParType)
1354 return rb_float_new(up->vdw.A14);
1355 else if (tp == kVdwPairParType)
1356 return rb_float_new(up->vdwp.A14);
1357 else rb_raise(rb_eMolbyError, "invalid member A14");
1365 * Get the "B" value for the 1-4 van der Waals parameter.
1368 static VALUE s_ParameterRef_GetB14(VALUE self) {
1371 up = s_UnionParFromValue(self, &tp, 0);
1372 if (tp == kVdwParType)
1373 return rb_float_new(up->vdw.B14);
1374 else if (tp == kVdwPairParType)
1375 return rb_float_new(up->vdwp.B14);
1376 else rb_raise(rb_eMolbyError, "invalid member B14");
1384 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1386 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1389 /* Double a, b, r; */
1391 up = s_UnionParFromValue(self, &tp, 0);
1392 if (tp == kVdwParType) {
1396 } else if (tp == kVdwPairParType) {
1397 /* a = up->vdwp.A14;
1398 b = up->vdwp.B14; */
1399 r = up->vdwp.r_eq14;
1400 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1401 /* if (a == 0.0 || b == 0.0) */
1402 return rb_float_new(r);
1403 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1410 * Get the minimum energy for the 1-4 van der Waals parameter.
1412 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1417 up = s_UnionParFromValue(self, &tp, 0);
1418 if (tp == kVdwParType) {
1421 eps = up->vdw.eps14;
1422 } else if (tp == kVdwPairParType) {
1423 /* a = up->vdwp.A14;
1424 b = up->vdwp.B14; */
1425 eps = up->vdwp.eps14;
1426 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1427 /* if (a == 0.0 || b == 0.0) */
1428 return rb_float_new(eps * INTERNAL2KCAL);
1429 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1436 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1438 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1441 up = s_UnionParFromValue(self, &tp, 0);
1442 if (tp == kVdwCutoffParType)
1443 return rb_float_new(up->vdwcutoff.cutoff);
1444 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1451 * Get the atomic radius for the atom display parameter.
1453 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1456 up = s_UnionParFromValue(self, &tp, 0);
1457 if (tp == kElementParType)
1458 return rb_float_new(up->atom.radius);
1459 else rb_raise(rb_eMolbyError, "invalid member radius");
1464 * color -> [Float, Float, Float]
1466 * Get the rgb color for the atom display parameter.
1468 static VALUE s_ParameterRef_GetColor(VALUE self) {
1471 up = s_UnionParFromValue(self, &tp, 0);
1472 if (tp == kElementParType)
1473 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1474 else rb_raise(rb_eMolbyError, "invalid member color");
1479 * atomic_number -> Integer
1481 * Get the atomic number for the vdw or atom parameter.
1483 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1486 up = s_UnionParFromValue(self, &tp, 0);
1487 if (tp == kElementParType)
1488 return INT2NUM(up->atom.number);
1489 else if (tp == kVdwParType)
1490 return INT2NUM(up->vdw.atomicNumber);
1491 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1498 * Get the name for the atom display parameter.
1500 static VALUE s_ParameterRef_GetName(VALUE self) {
1503 up = s_UnionParFromValue(self, &tp, 0);
1504 if (tp == kElementParType) {
1506 strncpy(name, up->atom.name, 4);
1508 return rb_str_new2(name);
1509 } else rb_raise(rb_eMolbyError, "invalid member name");
1516 * Get the atomic weight for the atom display parameter.
1518 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1521 up = s_UnionParFromValue(self, &tp, 0);
1522 if (tp == kElementParType)
1523 return rb_float_new(up->atom.weight);
1524 else if (tp == kVdwParType)
1525 return rb_float_new(up->vdw.weight);
1526 else rb_raise(rb_eMolbyError, "invalid member weight");
1531 * fullname -> String
1533 * Get the full name for the atom display parameter.
1535 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1538 up = s_UnionParFromValue(self, &tp, 0);
1539 if (tp == kElementParType) {
1541 strncpy(fullname, up->atom.fullname, 15);
1543 return rb_str_new2(fullname);
1544 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1551 * Get the comment for the parameter.
1553 static VALUE s_ParameterRef_GetComment(VALUE self) {
1556 up = s_UnionParFromValue(self, &tp, 0);
1560 else return rb_str_new2(ParameterGetComment(com));
1567 * Get the source string for the parameter. Returns false for undefined parameter,
1568 * and nil for "local" parameter that is specific for the molecule.
1570 static VALUE s_ParameterRef_GetSource(VALUE self) {
1573 up = s_UnionParFromValue(self, &tp, 0);
1576 return Qfalse; /* undefined */
1578 return Qnil; /* local */
1579 else return rb_str_new2(ParameterGetComment(src));
1583 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1590 if (rb_obj_is_kind_of(val, rb_cString)) {
1591 char *s = StringValuePtr(val);
1593 for (i = 0; i < n; i++) {
1596 /* Skip leading separaters */
1597 while (*s == '-' || *s == ' ' || *s == '\t')
1599 for (p = s; *p != 0; p++) {
1600 if (*p == '-' || *p == ' ' || *p == '\t')
1604 if (len >= sizeof(buf))
1605 len = sizeof(buf) - 1;
1606 strncpy(buf, s, len);
1608 /* Skip trailing blanks */
1609 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1612 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1613 if (buf[0] >= '0' && buf[0] <= '9')
1614 types[i] = atoi(buf);
1616 types[i] = AtomTypeEncodeToUInt(buf);
1617 if (p == NULL || *p == 0) {
1623 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1626 val = rb_ary_to_ary(val);
1627 if (RARRAY_LEN(val) != n)
1628 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1629 valp = RARRAY_PTR(val);
1631 for (i = 0; i < n; i++) {
1632 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1633 types[i] = NUM2INT(rb_Integer(valp[i]));
1635 VALUE sval = valp[i];
1636 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1641 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1646 up = s_UnionParFromValue(self, &tp, 1);
1647 oldval = s_ParameterRef_GetAtomTypes(self);
1648 oldsrc = up->bond.src;
1651 s_ScanAtomTypes(val, 2, types);
1652 up->bond.type1 = types[0];
1653 up->bond.type2 = types[1];
1656 s_ScanAtomTypes(val, 3, types);
1657 up->angle.type1 = types[0];
1658 up->angle.type2 = types[1];
1659 up->angle.type3 = types[2];
1661 case kDihedralParType:
1662 case kImproperParType:
1663 s_ScanAtomTypes(val, 4, types);
1664 up->torsion.type1 = types[0];
1665 up->torsion.type2 = types[1];
1666 up->torsion.type3 = types[2];
1667 up->torsion.type4 = types[3];
1670 s_ScanAtomTypes(val, 1, types);
1671 up->vdw.type1 = types[0];
1673 case kVdwPairParType:
1674 s_ScanAtomTypes(val, 2, types);
1675 up->vdwp.type1 = types[0];
1676 up->vdwp.type2 = types[1];
1678 case kVdwCutoffParType:
1679 s_ScanAtomTypes(val, 2, types);
1680 up->vdwcutoff.type1 = types[0];
1681 up->vdwcutoff.type2 = types[1];
1686 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1690 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1692 Int tp, i, n, oldsrc;
1693 VALUE *valp, oldval;
1694 up = s_UnionParFromValue(self, &tp, 1);
1695 oldval = s_ParameterRef_GetK(self);
1696 oldsrc = up->bond.src;
1699 val = rb_Float(val);
1700 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1703 val = rb_Float(val);
1704 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1706 case kDihedralParType:
1707 case kImproperParType:
1708 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1709 up->torsion.mult = 1;
1710 val = rb_Float(val);
1711 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1714 n = up->torsion.mult;
1717 val = rb_ary_to_ary(val);
1718 if (RARRAY_LEN(val) != n)
1719 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1720 valp = RARRAY_PTR(val);
1721 for (i = 0; i < n; i++) {
1722 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1726 rb_raise(rb_eMolbyError, "invalid member k");
1728 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1732 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1736 up = s_UnionParFromValue(self, &tp, 1);
1737 oldval = s_ParameterRef_GetR0(self);
1738 oldsrc = up->bond.src;
1739 if (tp == kBondParType) {
1740 val = rb_Float(val);
1741 up->bond.r0 = NUM2DBL(val);
1742 } else rb_raise(rb_eMolbyError, "invalid member r0");
1743 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1747 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1751 up = s_UnionParFromValue(self, &tp, 1);
1752 oldval = s_ParameterRef_GetA0(self);
1753 oldsrc = up->bond.src;
1754 if (tp == kAngleParType) {
1755 val = rb_Float(val);
1756 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1757 } else rb_raise(rb_eMolbyError, "invalid member a0");
1758 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1762 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1766 up = s_UnionParFromValue(self, &tp, 1);
1767 oldval = s_ParameterRef_GetMult(self);
1768 oldsrc = up->bond.src;
1769 if (tp == kDihedralParType || tp == kImproperParType) {
1771 val = rb_Integer(val);
1774 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1775 up->torsion.mult = i;
1776 } else rb_raise(rb_eMolbyError, "invalid member mult");
1777 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1781 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1783 Int tp, i, n, oldsrc;
1784 VALUE *valp, oldval;
1785 up = s_UnionParFromValue(self, &tp, 1);
1786 oldval = s_ParameterRef_GetPeriod(self);
1787 oldsrc = up->bond.src;
1788 if (tp == kDihedralParType || tp == kImproperParType) {
1789 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1790 up->torsion.mult = 1;
1791 val = rb_Integer(val);
1792 up->torsion.period[0] = NUM2INT(val);
1794 n = up->torsion.mult;
1797 val = rb_ary_to_ary(val);
1798 if (RARRAY_LEN(val) != n)
1799 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1800 valp = RARRAY_PTR(val);
1801 for (i = 0; i < n; i++) {
1802 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1805 } else rb_raise(rb_eMolbyError, "invalid member period");
1806 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1810 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1812 Int tp, i, n, oldsrc;
1813 VALUE *valp, oldval;
1814 up = s_UnionParFromValue(self, &tp, 1);
1815 oldval = s_ParameterRef_GetPhi0(self);
1816 oldsrc = up->bond.src;
1817 if (tp == kDihedralParType || tp == kImproperParType) {
1818 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1819 up->torsion.mult = 1;
1820 val = rb_Float(val);
1821 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1823 n = up->torsion.mult;
1826 val = rb_ary_to_ary(val);
1827 if (RARRAY_LEN(val) != n)
1828 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1829 valp = RARRAY_PTR(val);
1830 for (i = 0; i < n; i++)
1831 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1833 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1834 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1839 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1844 up = s_UnionParFromValue(self, &tp, 1);
1845 oldval = s_ParameterRef_GetA(self);
1846 oldsrc = up->bond.src;
1847 val = rb_Float(val);
1849 if (tp == kVdwParType)
1851 else if (tp == kVdwPairParType)
1853 else rb_raise(rb_eMolbyError, "invalid member A");
1854 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1858 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1863 up = s_UnionParFromValue(self, &tp, 1);
1864 oldval = s_ParameterRef_GetB(self);
1865 oldsrc = up->bond.src;
1866 val = rb_Float(val);
1868 if (tp == kVdwParType)
1870 else if (tp == kVdwPairParType)
1872 else rb_raise(rb_eMolbyError, "invalid member B");
1873 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1878 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1883 up = s_UnionParFromValue(self, &tp, 1);
1884 oldval = s_ParameterRef_GetReq(self);
1885 oldsrc = up->bond.src;
1886 val = rb_Float(val);
1888 if (tp == kVdwParType) {
1890 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1891 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1892 } else if (tp == kVdwPairParType) {
1894 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1895 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1896 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1897 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1901 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1906 up = s_UnionParFromValue(self, &tp, 1);
1907 oldval = s_ParameterRef_GetEps(self);
1908 oldsrc = up->bond.src;
1909 val = rb_Float(val);
1910 e = NUM2DBL(val) * KCAL2INTERNAL;
1911 if (tp == kVdwParType) {
1913 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1914 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1915 } else if (tp == kVdwPairParType) {
1917 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1918 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1919 } else rb_raise(rb_eMolbyError, "invalid member eps");
1920 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1925 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1930 up = s_UnionParFromValue(self, &tp, 1);
1931 oldval = s_ParameterRef_GetA14(self);
1932 oldsrc = up->bond.src;
1933 val = rb_Float(val);
1935 if (tp == kVdwParType)
1937 else if (tp == kVdwPairParType)
1939 else rb_raise(rb_eMolbyError, "invalid member A14");
1940 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
1944 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1949 up = s_UnionParFromValue(self, &tp, 1);
1950 oldval = s_ParameterRef_GetB14(self);
1951 oldsrc = up->bond.src;
1952 val = rb_Float(val);
1954 if (tp == kVdwParType)
1956 else if (tp == kVdwPairParType)
1958 else rb_raise(rb_eMolbyError, "invalid member B14");
1959 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
1964 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1969 up = s_UnionParFromValue(self, &tp, 1);
1970 oldval = s_ParameterRef_GetReq14(self);
1971 oldsrc = up->bond.src;
1972 val = rb_Float(val);
1974 if (tp == kVdwParType) {
1976 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1977 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1978 } else if (tp == kVdwPairParType) {
1979 up->vdwp.r_eq14 = r;
1980 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1981 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1982 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1983 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
1987 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1992 up = s_UnionParFromValue(self, &tp, 1);
1993 oldval = s_ParameterRef_GetEps14(self);
1994 oldsrc = up->bond.src;
1995 val = rb_Float(val);
1996 e = NUM2DBL(val) * KCAL2INTERNAL;
1997 if (tp == kVdwParType) {
1999 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
2000 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
2001 } else if (tp == kVdwPairParType) {
2003 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
2004 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
2005 } else rb_raise(rb_eMolbyError, "invalid member eps14");
2006 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
2010 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
2014 oldval = s_ParameterRef_GetCutoff(self);
2015 oldsrc = up->bond.src;
2016 up = s_UnionParFromValue(self, &tp, 1);
2017 val = rb_Float(val);
2018 if (tp == kVdwCutoffParType) {
2019 up->vdwcutoff.cutoff = NUM2DBL(val);
2020 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
2021 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
2025 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
2029 up = s_UnionParFromValue(self, &tp, 1);
2030 oldval = s_ParameterRef_GetRadius(self);
2031 oldsrc = up->bond.src;
2032 val = rb_Float(val);
2033 if (tp == kElementParType) {
2034 up->atom.radius = NUM2DBL(val);
2035 } else rb_raise(rb_eMolbyError, "invalid member radius");
2036 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
2040 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
2043 VALUE *valp, oldval;
2044 up = s_UnionParFromValue(self, &tp, 1);
2045 oldval = s_ParameterRef_GetColor(self);
2046 oldsrc = up->bond.src;
2047 val = rb_ary_to_ary(val);
2048 if (RARRAY_LEN(val) != 3)
2049 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
2050 valp = RARRAY_PTR(val);
2051 if (tp == kElementParType) {
2052 up->atom.r = NUM2DBL(rb_Float(valp[0]));
2053 up->atom.g = NUM2DBL(rb_Float(valp[1]));
2054 up->atom.b = NUM2DBL(rb_Float(valp[2]));
2055 } else rb_raise(rb_eMolbyError, "invalid member color");
2056 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
2060 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
2064 up = s_UnionParFromValue(self, &tp, 1);
2065 oldval = s_ParameterRef_GetAtomicNumber(self);
2066 oldsrc = up->bond.src;
2067 val = rb_Integer(val);
2068 if (tp == kElementParType)
2069 up->atom.number = NUM2INT(val);
2070 else if (tp == kVdwParType) {
2071 up->vdw.atomicNumber = NUM2INT(val);
2072 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
2073 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
2074 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
2078 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
2082 up = s_UnionParFromValue(self, &tp, 1);
2083 oldval = s_ParameterRef_GetName(self);
2084 oldsrc = up->bond.src;
2085 if (tp == kElementParType) {
2086 strncpy(up->atom.name, StringValuePtr(val), 4);
2087 } else rb_raise(rb_eMolbyError, "invalid member name");
2088 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
2092 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
2096 val = rb_Float(val);
2097 oldval = s_ParameterRef_GetWeight(self);
2098 up = s_UnionParFromValue(self, &tp, 1);
2099 oldsrc = up->bond.src;
2100 if (tp == kElementParType)
2101 up->atom.weight = NUM2DBL(val);
2102 else if (tp == kVdwParType)
2103 up->vdw.weight = NUM2DBL(val);
2104 else rb_raise(rb_eMolbyError, "invalid member weight");
2105 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
2109 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
2113 up = s_UnionParFromValue(self, &tp, 1);
2114 oldval = s_ParameterRef_GetFullName(self);
2115 oldsrc = up->bond.src;
2116 if (tp == kElementParType) {
2117 strncpy(up->atom.fullname, StringValuePtr(val), 15);
2118 up->atom.fullname[15] = 0;
2119 } else rb_raise(rb_eMolbyError, "invalid member fullname");
2120 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
2124 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
2126 Int tp, com, oldsrc;
2128 up = s_UnionParFromValue(self, &tp, 1);
2129 oldval = s_ParameterRef_GetComment(self);
2130 oldsrc = up->bond.src;
2134 com = ParameterCommentIndex(StringValuePtr(val));
2137 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
2141 /* Only false (undefined) and nil (local) can be set */
2142 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
2146 up = s_UnionParFromValue(self, &tp, 1);
2147 if (val != Qfalse && val != Qnil)
2148 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
2149 oldval = s_ParameterRef_GetSource(self);
2150 oldsrc = up->bond.src;
2151 if (oldsrc != 0 && oldsrc != -1)
2152 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
2153 up->bond.src = (val == Qfalse ? -1 : 0);
2154 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
2158 static struct s_ParameterAttrDef {
2160 VALUE *symref; /* Address of s_IndexSymbol etc. */
2161 ID id; /* Will be set within InitMolby() */
2162 VALUE (*getter)(VALUE);
2163 VALUE (*setter)(VALUE, VALUE);
2164 } s_ParameterAttrDefTable[] = {
2165 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
2166 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
2167 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2168 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
2169 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
2170 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
2171 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
2172 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
2173 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
2174 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
2175 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
2176 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
2177 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
2178 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
2179 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
2180 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
2181 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
2182 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
2183 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
2184 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
2185 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
2186 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
2187 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
2188 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
2189 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
2190 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
2191 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
2192 {NULL} /* Sentinel */
2196 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
2200 if (TYPE(key) != T_SYMBOL) {
2201 kid = rb_intern(StringValuePtr(key));
2203 } else kid = SYM2ID(key);
2204 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2205 if (s_ParameterAttrDefTable[i].id == kid) {
2206 if (value == Qundef)
2207 return (*(s_ParameterAttrDefTable[i].getter))(self);
2208 else if (s_ParameterAttrDefTable[i].setter == NULL)
2209 rb_raise(rb_eMolbyError, "the attribute \"%s\" is read-only", rb_id2name(kid));
2211 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2214 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2215 return Qnil; /* not reached */
2219 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2221 return s_ParameterRef_SetAttr(self, key, Qundef);
2226 * keys(idx) -> array of valid parameter attributes
2228 * Returns an array of valid parameter attributes (as Symbols).
2231 s_ParameterRef_Keys(VALUE self)
2234 Data_Get_Struct(self, ParameterRef, pref);
2235 switch (pref->parType) {
2237 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2239 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2240 case kDihedralParType:
2241 case kImproperParType:
2242 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2244 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);
2245 case kVdwPairParType:
2246 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2247 case kVdwCutoffParType:
2248 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2249 case kElementParType:
2250 return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_CommentSym, s_SourceSym);
2252 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2254 return Qnil; /* Not reached */
2259 * to_hash(idx) -> Hash
2261 * Returns a hash containing valid parameter names and values
2264 s_ParameterRef_ToHash(VALUE self)
2266 VALUE keys = s_ParameterRef_Keys(self);
2271 retval = rb_hash_new();
2272 for (i = 0; i < RARRAY_LEN(keys); i++) {
2273 VALUE key = RARRAY_PTR(keys)[i];
2274 VALUE val = s_ParameterRef_GetAttr(self, key);
2275 rb_hash_aset(retval, key, val);
2282 * parameter.to_s(idx) -> String
2284 * Returns a string representation of the given parameter
2287 s_ParameterRef_ToString(VALUE self)
2290 char buf[1024], types[4][8];
2291 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2294 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);
2297 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);
2299 case kDihedralParType:
2300 case kImproperParType:
2301 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]));
2303 for (i = 0; i < up->torsion.mult; i++) {
2304 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);
2309 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);
2311 case kVdwPairParType:
2312 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);
2314 case kVdwCutoffParType:
2315 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);
2317 case kElementParType:
2318 snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s", up->atom.name, up->atom.number, up->atom.radius, up->atom.r, up->atom.g, up->atom.b, up->atom.weight, up->atom.fullname);
2321 return rb_str_new2(buf);
2326 * self == parameterRef -> boolean
2328 * True if the parameters point to the same parameter record.
2331 s_ParameterRef_Equal(VALUE self, VALUE val)
2334 if (rb_obj_is_kind_of(val, rb_cParameterRef)) {
2335 return (s_UnionParFromValue(self, &tp1, 0) == s_UnionParFromValue(val, &tp2, 0) ? Qtrue : Qfalse);
2336 } else return Qfalse;
2339 #pragma mark ====== Parameter Class ======
2341 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2342 * is NULL, then the global parameters are looked for. */
2344 /* Rebuild the MD parameter record if necessary: may throw an exception */
2345 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2347 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2350 Data_Get_Struct(val, Molecule, mol);
2352 rb_raise(rb_eMolbyError, "the molecule is empty");
2353 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2354 /* Do self.md_arena.prepare */
2355 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2357 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2362 s_NewParameterValueFromValue(VALUE val)
2365 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2366 Data_Get_Struct(val, Molecule, mol);
2367 s_RebuildMDParameterIfNecessary(val, Qtrue);
2368 MoleculeRetain(mol);
2369 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2372 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2377 s_MoleculeFromParameterValue(VALUE val)
2380 Data_Get_Struct(val, Molecule, mol);
2385 s_ParameterFromParameterValue(VALUE val)
2388 Data_Get_Struct(val, Molecule, mol);
2391 return gBuiltinParameters;
2394 /* Forward declarations */
2395 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2396 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2399 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2401 if (val == rb_cParameter) {
2402 return NULL; /* Parameter class method: builtin parameters */
2403 } else if (rb_obj_is_kind_of(val, rb_cParameter)) {
2404 return s_MoleculeFromParameterValue(val);
2405 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2406 return s_MoleculeFromParEnumerableValue(val);
2412 * builtin -> Parameter
2414 * Returns a parameter value that points to the global (builtin) parameters.
2415 * Equivalent to Parameter::Builtin (constant).
2418 s_Parameter_Builtin(VALUE self)
2420 static ID s_builtin_id = 0;
2421 if (s_builtin_id == 0)
2422 s_builtin_id = rb_intern("Builtin");
2423 return rb_const_get(rb_cParameter, s_builtin_id);
2428 * bond(idx) -> ParameterRef
2430 * The index-th bond parameter record is returned.
2433 s_Parameter_Bond(VALUE self, VALUE ival)
2437 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2438 idx = NUM2INT(rb_Integer(ival));
2440 n = gBuiltinParameters->nbondPars;
2441 else if (mol->par != NULL)
2442 n = mol->par->nbondPars;
2444 if (idx < -n || idx >= n)
2445 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2448 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2453 * angle(idx) -> ParameterRef
2455 * The index-th angle parameter record is returned.
2458 s_Parameter_Angle(VALUE self, VALUE ival)
2462 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2463 idx = NUM2INT(rb_Integer(ival));
2465 n = gBuiltinParameters->nanglePars;
2466 else if (mol->par != NULL)
2467 n = mol->par->nanglePars;
2469 if (idx < -n || idx >= n)
2470 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2473 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2478 * dihedral(idx) -> ParameterRef
2480 * The index-th dihedral parameter record is returned.
2483 s_Parameter_Dihedral(VALUE self, VALUE ival)
2487 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2488 idx = NUM2INT(rb_Integer(ival));
2490 n = gBuiltinParameters->ndihedralPars;
2491 else if (mol->par != NULL)
2492 n = mol->par->ndihedralPars;
2494 if (idx < -n || idx >= n)
2495 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2498 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2503 * improper(idx) -> ParameterRef
2505 * The index-th improper parameter record is returned.
2508 s_Parameter_Improper(VALUE self, VALUE ival)
2512 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2513 idx = NUM2INT(rb_Integer(ival));
2515 n = gBuiltinParameters->nimproperPars;
2516 else if (mol->par != NULL)
2517 n = mol->par->nimproperPars;
2519 if (idx < -n || idx >= n)
2520 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2523 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2528 * vdw(idx) -> ParameterRef
2530 * The index-th vdw parameter record is returned.
2533 s_Parameter_Vdw(VALUE self, VALUE ival)
2537 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2538 idx = NUM2INT(rb_Integer(ival));
2540 n = gBuiltinParameters->nvdwPars;
2541 else if (mol->par != NULL)
2542 n = mol->par->nvdwPars;
2544 if (idx < -n || idx >= n)
2545 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2548 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2553 * vdw_pair(idx) -> ParameterRef
2555 * The index-th vdw pair parameter record is returned.
2558 s_Parameter_VdwPair(VALUE self, VALUE ival)
2562 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2563 idx = NUM2INT(rb_Integer(ival));
2565 n = gBuiltinParameters->nvdwpPars;
2566 else if (mol->par != NULL)
2567 n = mol->par->nvdwpPars;
2569 if (idx < -n || idx >= n)
2570 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2573 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2578 * vdw_cutoff(idx) -> ParameterRef
2580 * The index-th vdw cutoff parameter record is returned.
2583 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2587 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2588 idx = NUM2INT(rb_Integer(ival));
2590 n = gBuiltinParameters->nvdwCutoffPars;
2591 else if (mol->par != NULL)
2592 n = mol->par->nvdwCutoffPars;
2594 if (idx < -n || idx >= n)
2595 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2598 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2603 * element(idx) -> ParameterRef
2604 * element(t1) -> ParameterRef
2606 * In the first form, the index-th element parameter record is returned. In the second
2607 * form, the element parameter for t1 is looked up (the last index first). t1
2608 * is the element name string (up to 4 characters).
2609 * Unlike other Parameter methods, this is used only for the global parameter.
2612 s_Parameter_Element(VALUE self, VALUE ival)
2615 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2616 int n = gCountElementParameters;
2617 idx1 = NUM2INT(rb_Integer(ival));
2618 if (idx1 < -n || idx1 >= n)
2619 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2622 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2627 strncpy(name, StringValuePtr(ival), 4);
2629 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--, ep--) {
2630 if (strncmp(ep->name, name, 4) == 0)
2631 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2641 * Returns the number of bond parameters.
2644 s_Parameter_Nbonds(VALUE self)
2647 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2649 n = gBuiltinParameters->nbondPars;
2650 else if (mol->par != NULL)
2651 n = mol->par->nbondPars;
2658 * nangles -> Integer
2660 * Returns the number of angle parameters.
2663 s_Parameter_Nangles(VALUE self)
2666 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2668 n = gBuiltinParameters->nanglePars;
2669 else if (mol->par != NULL)
2670 n = mol->par->nanglePars;
2677 * ndihedrals -> Integer
2679 * Returns the number of dihedral parameters.
2682 s_Parameter_Ndihedrals(VALUE self)
2685 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2687 n = gBuiltinParameters->ndihedralPars;
2688 else if (mol->par != NULL)
2689 n = mol->par->ndihedralPars;
2696 * nimpropers -> Integer
2698 * Returns the number of improper parameters.
2701 s_Parameter_Nimpropers(VALUE self)
2704 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2706 n = gBuiltinParameters->nimproperPars;
2707 else if (mol->par != NULL)
2708 n = mol->par->nimproperPars;
2717 * Returns the number of vdw parameters.
2720 s_Parameter_Nvdws(VALUE self)
2723 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2725 n = gBuiltinParameters->nvdwPars;
2726 else if (mol->par != NULL)
2727 n = mol->par->nvdwPars;
2734 * nvdw_pairs -> Integer
2736 * Returns the number of vdw pair parameters.
2739 s_Parameter_NvdwPairs(VALUE self)
2742 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2744 n = gBuiltinParameters->nvdwpPars;
2745 else if (mol->par != NULL)
2746 n = mol->par->nvdwpPars;
2753 * nvdw_cutoffs -> Integer
2755 * Returns the number of vdw cutoff parameters.
2758 s_Parameter_NvdwCutoffs(VALUE self)
2761 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2763 n = gBuiltinParameters->nvdwCutoffPars;
2764 else if (mol->par != NULL)
2765 n = mol->par->nvdwCutoffPars;
2772 * nelements -> Integer
2774 * Returns the number of element parameters.
2777 s_Parameter_Nelements(VALUE self)
2779 return INT2NUM(gCountElementParameters);
2784 * bonds -> ParEnumerable
2786 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2787 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2788 * useful when all accessible parameters should be examined by use of 'each' method.
2791 s_Parameter_Bonds(VALUE self)
2793 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2794 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2799 * angles -> ParEnumerable
2801 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2802 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2803 * useful when all accessible parameters should be examined by use of 'each' method.
2806 s_Parameter_Angles(VALUE self)
2808 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2809 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2814 * dihedrals -> ParEnumerable
2816 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2817 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2818 * useful when all accessible parameters should be examined by use of 'each' method.
2821 s_Parameter_Dihedrals(VALUE self)
2823 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2824 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2829 * impropers -> ParEnumerable
2831 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2832 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2833 * useful when all accessible parameters should be examined by use of 'each' method.
2836 s_Parameter_Impropers(VALUE self)
2838 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2839 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2844 * vdws -> ParEnumerable
2846 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2847 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2848 * useful when all accessible parameters should be examined by use of 'each' method.
2851 s_Parameter_Vdws(VALUE self)
2853 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2854 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2859 * vdw_pairs -> ParEnumerable
2861 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2862 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2863 * useful when all accessible parameters should be examined by use of 'each' method.
2866 s_Parameter_VdwPairs(VALUE self)
2868 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2869 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2874 * vdw_cutoffs -> ParEnumerable
2876 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2877 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2878 * useful when all accessible parameters should be examined by use of 'each' method.
2881 s_Parameter_VdwCutoffs(VALUE self)
2883 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2884 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2889 * elements -> ParEnumerable
2891 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2892 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2893 * useful when all accessible parameters should be examined by use of 'each' method.
2896 s_Parameter_Elements(VALUE self)
2898 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2899 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2903 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2905 VALUE atval, optval;
2908 int i, n, idx, flags, is_global;
2910 rb_scan_args(argc, argv, "1*", &atval, &optval);
2912 /* Get the atom types */
2914 case kBondParType: n = 2; break;
2915 case kAngleParType: n = 3; break;
2916 case kDihedralParType: n = 4; break;
2917 case kImproperParType: n = 4; break;
2918 case kVdwParType: n = 1; break;
2919 case kVdwPairParType: n = 2; break;
2920 default: return Qnil;
2922 s_ScanAtomTypes(atval, n, t);
2923 for (i = 0; i < n; i++) {
2924 if (t[i] < kAtomTypeMinimum) {
2925 /* Explicit atom index */
2927 rb_raise(rb_eMolbyError, "Explicit atom index (%d) is invalid for global parameters", t[i]);
2928 if (t[i] >= mol->natoms)
2929 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", t[i]);
2931 t[i] = ATOM_AT_INDEX(mol->atoms, t[i])->type;
2935 /* Analyze options */
2937 n = RARRAY_LEN(optval);
2938 for (i = 0; i < n; i++) {
2939 VALUE oval = RARRAY_PTR(optval)[i];
2940 if (oval == ID2SYM(rb_intern("global")))
2941 flags |= kParameterLookupGlobal;
2942 else if (oval == ID2SYM(rb_intern("local")))
2943 flags |= kParameterLookupLocal;
2944 else if (oval == ID2SYM(rb_intern("missing")))
2945 flags |= kParameterLookupMissing;
2946 else if (oval == ID2SYM(rb_intern("nowildcard")))
2947 flags |= kParameterLookupNoWildcard;
2948 else if (oval == ID2SYM(rb_intern("nobasetype")))
2949 flags |= kParameterLookupNoBaseAtomType;
2950 else if (oval == ID2SYM(rb_intern("create")))
2954 flags = kParameterLookupGlobal | kParameterLookupLocal;
2959 case kBondParType: {
2962 bp = ParameterLookupBondPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
2964 idx = bp - mol->par->bondPars;
2968 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2970 idx = bp - gBuiltinParameters->bondPars;
2975 case kAngleParType: {
2978 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], ii[0], ii[1], ii[2], flags);
2980 idx = ap - mol->par->anglePars;
2984 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2986 idx = ap - gBuiltinParameters->anglePars;
2991 case kDihedralParType: {
2994 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
2996 idx = tp - mol->par->dihedralPars;
3000 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3002 idx = tp - gBuiltinParameters->dihedralPars;
3007 case kImproperParType: {
3010 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], ii[0], ii[1], ii[2], ii[3], flags);
3012 idx = tp - mol->par->improperPars;
3016 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
3018 idx = tp - gBuiltinParameters->improperPars;
3026 vp = ParameterLookupVdwPar(mol->par, t[0], ii[0], flags);
3028 idx = vp - mol->par->vdwPars;
3032 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], -1, flags);
3034 idx = vp - gBuiltinParameters->vdwPars;
3039 case kVdwPairParType: {
3042 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], ii[0], ii[1], flags);
3044 idx = vp - mol->par->vdwpPars;
3048 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
3050 idx = vp - gBuiltinParameters->vdwpPars;
3059 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
3062 /* Insert a new parameter record */
3064 Int count = ParameterGetCountForType(mol->par, parType);
3065 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
3066 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
3067 IntGroupRelease(ig);
3070 /* Set atom types */
3071 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
3076 up->bond.type1 = t[0];
3077 up->bond.type2 = t[1];
3080 up->angle.type1 = t[0];
3081 up->angle.type2 = t[1];
3082 up->angle.type3 = t[2];
3084 case kDihedralParType:
3085 case kImproperParType:
3086 up->torsion.type1 = t[0];
3087 up->torsion.type2 = t[1];
3088 up->torsion.type3 = t[2];
3089 up->torsion.type4 = t[3];
3092 up->vdw.type1 = t[0];
3094 case kVdwPairParType:
3095 up->vdwp.type1 = t[0];
3096 up->vdwp.type2 = t[1];
3103 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
3108 * lookup(par_type, atom_types, options, ...) -> ParameterRef
3109 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
3111 * Find the parameter record that matches the given atom types. The atom types are given
3112 * either as an array of string, or a single string delimited by whitespaces or hyphens.
3113 * Options are given as symbols. Valid values are :global (look for global parameters), :local
3114 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
3115 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
3118 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
3121 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
3123 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
3124 parType = s_ParTypeFromValue(argv[0]);
3125 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
3130 * self == parameter -> boolean
3132 * True if the parameters point to the same parameter table.
3135 s_Parameter_Equal(VALUE self, VALUE val)
3137 if (rb_obj_is_kind_of(val, rb_cParameter)) {
3138 return (s_MoleculeFromParameterOrParEnumerableValue(self) == s_MoleculeFromParameterOrParEnumerableValue(val) ? Qtrue : Qfalse);
3139 } else return Qfalse;
3142 #pragma mark ====== ParEnumerable Class ======
3144 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
3145 and the parameter type. If the Molecule is NULL, then it refers to the
3146 global (built-in) parameters. Note that, even when the Molecule is not NULL,
3147 the global parameters are always accessible. */
3149 typedef struct ParEnumerable {
3151 Int parType; /* Same as parType in ParameterRef */
3154 static ParEnumerable *
3155 s_ParEnumerableNew(Molecule *mol, Int parType)
3157 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
3161 MoleculeRetain(mol);
3162 pen->parType = parType;
3168 s_ParEnumerableRelease(ParEnumerable *pen)
3171 if (pen->mol != NULL)
3172 MoleculeRelease(pen->mol);
3178 s_MoleculeFromParEnumerableValue(VALUE val)
3181 Data_Get_Struct(val, ParEnumerable, pen);
3186 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
3188 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
3190 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
3191 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
3196 * par_type -> String
3198 * Get the parameter type, like "bond", "angle", etc.
3201 s_ParEnumerable_ParType(VALUE self) {
3204 Data_Get_Struct(self, ParEnumerable, pen);
3206 if (tp == kElementParType)
3207 return rb_str_new2("element");
3208 tp -= kFirstParType;
3209 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
3210 return rb_str_new2(s_ParameterTypeNames[tp]);
3211 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
3216 * self[idx] -> ParameterRef
3218 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
3219 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
3220 * parent Parameter object of self.
3222 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
3223 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
3226 s_ParEnumerable_Aref(VALUE self, VALUE ival)
3229 Data_Get_Struct(self, ParEnumerable, pen);
3230 switch (pen->parType) {
3231 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3232 case kBondParType: return s_Parameter_Bond(self, ival);
3233 case kAngleParType: return s_Parameter_Angle(self, ival);
3234 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
3235 case kImproperParType: return s_Parameter_Improper(self, ival);
3236 case kVdwParType: return s_Parameter_Vdw(self, ival);
3237 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
3238 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
3239 case kElementParType: return s_Parameter_Element(self, ival);
3241 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3243 return Qnil; /* Not reached */
3250 * Returns the number of parameters included in this enumerable.
3253 s_ParEnumerable_Length(VALUE self)
3256 Data_Get_Struct(self, ParEnumerable, pen);
3257 switch (pen->parType) {
3258 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3259 case kBondParType: return s_Parameter_Nbonds(self);
3260 case kAngleParType: return s_Parameter_Nangles(self);
3261 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3262 case kImproperParType: return s_Parameter_Nimpropers(self);
3263 case kVdwParType: return s_Parameter_Nvdws(self);
3264 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3265 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3266 case kElementParType: return s_Parameter_Nelements(self);
3268 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3270 return Qnil; /* Not reached */
3277 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3280 s_ParEnumerable_Each(VALUE self)
3286 Data_Get_Struct(self, ParEnumerable, pen);
3287 if (pen->parType == kElementParType)
3288 n = gCountElementParameters;
3290 switch (pen->parType) {
3291 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3292 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3293 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3294 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3295 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3296 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3297 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3299 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3301 if (pen->mol == NULL)
3302 n = *((Int *)((char *)gBuiltinParameters + ofs));
3303 else if (pen->mol->par != NULL)
3304 n = *((Int *)((char *)(pen->mol->par) + ofs));
3307 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3308 Data_Get_Struct(aval, ParameterRef, pref);
3309 for (i = 0; i < n; i++) {
3318 * reverse_each {|pref| ...}
3320 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3323 s_ParEnumerable_ReverseEach(VALUE self)
3329 Data_Get_Struct(self, ParEnumerable, pen);
3330 if (pen->parType == kElementParType)
3331 n = gCountElementParameters;
3333 switch (pen->parType) {
3334 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3335 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3336 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3337 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3338 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3339 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3340 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3342 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3344 if (pen->mol == NULL)
3345 n = *((Int *)((char *)gBuiltinParameters + ofs));
3346 else if (pen->mol->par != NULL)
3347 n = *((Int *)((char *)(pen->mol->par) + ofs));
3350 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3351 Data_Get_Struct(aval, ParameterRef, pref);
3352 for (i = n - 1; i >= 0; i--) {
3361 * insert(idx = nil, pref = nil) -> ParameterRef
3363 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3364 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3365 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3366 * parameter is left undefined.
3367 * Throws an exception if ParEnumerable points to the global parameter.
3370 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3378 Data_Get_Struct(self, ParEnumerable, pen);
3379 if (pen->mol == NULL)
3380 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3381 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3382 rb_scan_args(argc, argv, "02", &ival, &pval);
3384 i = NUM2INT(rb_Integer(ival));
3386 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3391 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3392 if (up == NULL || type != pen->parType)
3393 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3394 ParameterCopyOneWithType(&u, up, pen->parType);
3397 memset(&u, 0, sizeof(u));
3400 ig = IntGroupNewWithPoints(n, 1, -1);
3401 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3403 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3404 MolActionCallback_registerUndo(pen->mol, act);
3405 MolActionRelease(act);
3407 IntGroupRelease(ig);
3408 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3416 * Delete the parameter(s) specified by the argument.
3419 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3424 Data_Get_Struct(self, ParEnumerable, pen);
3425 if (pen->mol == NULL)
3426 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3427 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3428 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3429 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3432 ig = IntGroupFromValue(ival);
3433 if ((i = IntGroupGetCount(ig)) == 0) {
3434 IntGroupRelease(ig);
3438 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3439 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3442 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3443 IntGroupRelease(ig);
3449 * lookup(atom_types, options, ...) -> ParameterRef
3450 * lookup(atom_type_string, options, ...) -> ParameterRef
3452 * Find the parameter record that matches the given atom types. The arguments are
3453 * the same as Parameter#lookup, except for the parameter type which is implicitly
3457 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3460 Data_Get_Struct(self, ParEnumerable, pen);
3461 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3466 * self == parEnumerable -> boolean
3468 * True if the arguments point to the same parameter table and type.
3471 s_ParEnumerable_Equal(VALUE self, VALUE val)
3473 if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
3474 ParEnumerable *pen1, *pen2;
3475 Data_Get_Struct(self, ParEnumerable, pen1);
3476 Data_Get_Struct(val, ParEnumerable, pen2);
3477 return (pen1->mol == pen2->mol && pen1->parType == pen2->parType) ? Qtrue : Qfalse;
3478 } else return Qfalse;
3481 #pragma mark ====== AtomRef Class ======
3483 /* Forward declaration for register undo */
3484 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3486 /* Ruby string "set_atom_attr" */
3487 static VALUE s_SetAtomAttrString;
3490 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3494 Data_Get_Struct(self, AtomRef, aref);
3495 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3496 if (idx < 0 || idx >= aref->mol->natoms)
3497 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3499 *app = aref->mol->atoms + idx;
3506 s_AtomFromValue(VALUE self)
3509 s_AtomIndexFromValue(self, &ap, NULL);
3514 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3517 s_AtomIndexFromValue(self, &ap, mpp);
3522 s_NotifyModificationForAtomRef(VALUE self)
3525 Data_Get_Struct(self, AtomRef, aref);
3526 MoleculeIncrementModifyCount(aref->mol);
3530 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3533 Data_Get_Struct(self, AtomRef, aref);
3534 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3537 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3538 MolActionCallback_registerUndo(aref->mol, act);
3539 MoleculeCallback_notifyModification(aref->mol, 0);
3540 /* Request MD rebuilt if necessary */
3541 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3542 aref->mol->needsMDRebuild = 1;
3547 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3550 aref = AtomRefNew(mol, idx);
3551 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3555 s_AtomRef_GetMolecule(VALUE self)
3558 s_AtomIndexFromValue(self, NULL, &mpp);
3559 return ValueFromMolecule(mpp);
3562 static VALUE s_AtomRef_GetIndex(VALUE self) {
3563 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3566 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3567 return INT2NUM(s_AtomFromValue(self)->segSeq);
3570 static VALUE s_AtomRef_GetSegName(VALUE self) {
3571 char *p = s_AtomFromValue(self)->segName;
3572 return rb_str_new(p, strlen_limit(p, 4));
3575 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3576 return INT2NUM(s_AtomFromValue(self)->resSeq);
3579 static VALUE s_AtomRef_GetResName(VALUE self) {
3580 char *p = s_AtomFromValue(self)->resName;
3581 return rb_str_new(p, strlen_limit(p, 4));
3584 static VALUE s_AtomRef_GetName(VALUE self) {
3585 char *p = s_AtomFromValue(self)->aname;
3586 return rb_str_new(p, strlen_limit(p, 4));
3589 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3590 int type = s_AtomFromValue(self)->type;
3591 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3592 return rb_str_new(p, strlen_limit(p, 6));
3595 static VALUE s_AtomRef_GetCharge(VALUE self) {
3596 return rb_float_new(s_AtomFromValue(self)->charge);
3599 static VALUE s_AtomRef_GetWeight(VALUE self) {
3600 return rb_float_new(s_AtomFromValue(self)->weight);
3603 static VALUE s_AtomRef_GetElement(VALUE self) {
3604 char *p = s_AtomFromValue(self)->element;
3605 return rb_str_new(p, strlen_limit(p, 4));
3608 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3609 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3612 static VALUE s_AtomRef_GetConnects(VALUE self) {
3615 Atom *ap = s_AtomFromValue(self);
3616 retval = rb_ary_new();
3617 cp = AtomConnectData(&ap->connect);
3618 for (i = 0; i < ap->connect.count; i++)
3619 rb_ary_push(retval, INT2NUM(cp[i]));
3623 static VALUE s_AtomRef_GetR(VALUE self) {
3624 return ValueFromVector(&(s_AtomFromValue(self)->r));
3627 static VALUE s_AtomRef_GetX(VALUE self) {
3628 return rb_float_new(s_AtomFromValue(self)->r.x);
3631 static VALUE s_AtomRef_GetY(VALUE self) {
3632 return rb_float_new(s_AtomFromValue(self)->r.y);
3635 static VALUE s_AtomRef_GetZ(VALUE self) {
3636 return rb_float_new(s_AtomFromValue(self)->r.z);
3639 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3643 s_AtomIndexFromValue(self, &ap, &mp);
3645 if (mp->cell != NULL)
3646 TransformVec(&r1, mp->cell->rtr, &r1);
3650 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3651 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3652 return ValueFromVector(&r1);
3655 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3656 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3659 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3660 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3663 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3664 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3667 static VALUE s_AtomRef_GetSigma(VALUE self) {
3668 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3671 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3672 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3675 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3676 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3679 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3680 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3683 static VALUE s_AtomRef_GetV(VALUE self) {
3684 return ValueFromVector(&(s_AtomFromValue(self)->v));
3687 static VALUE s_AtomRef_GetF(VALUE self) {
3688 Vector v = s_AtomFromValue(self)->f;
3689 VecScaleSelf(v, INTERNAL2KCAL);
3690 return ValueFromVector(&v);
3693 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3694 return rb_float_new(s_AtomFromValue(self)->occupancy);
3697 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3698 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3701 static VALUE s_AtomRef_GetAniso(VALUE self) {
3704 Atom *ap = s_AtomFromValue(self);
3705 if (ap->aniso == NULL)
3707 retval = rb_ary_new();
3708 for (i = 0; i < 6; i++)
3709 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3710 if (ap->aniso->has_bsig) {
3711 rb_ary_push(retval, INT2NUM(0));
3712 for (i = 0; i < 6; i++)
3713 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3718 static VALUE s_AtomRef_GetSymop(VALUE self) {
3720 Atom *ap = s_AtomFromValue(self);
3721 if (!ap->symop.alive)
3723 retval = rb_ary_new();
3724 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3725 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3726 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3727 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3728 rb_ary_push(retval, INT2NUM(ap->symbase));
3732 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3733 return INT2NUM(s_AtomFromValue(self)->intCharge);
3736 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3737 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3740 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3741 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3744 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3748 MDExclusion *exinfo;
3751 idx = s_AtomIndexFromValue(self, &ap, &mol);
3752 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3753 VALUE mval = ValueFromMolecule(mol);
3754 s_RebuildMDParameterIfNecessary(mval, Qnil);
3756 if (mol->arena->exinfo == NULL)
3758 exinfo = mol->arena->exinfo + idx;
3759 exlist = mol->arena->exlist;
3760 retval = rb_ary_new();
3761 aval = rb_ary_new();
3762 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3763 rb_ary_push(aval, INT2NUM(exlist[i]));
3764 rb_ary_push(retval, aval);
3765 aval = rb_ary_new();
3766 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3767 rb_ary_push(aval, INT2NUM(exlist[i]));
3768 rb_ary_push(retval, aval);
3769 aval = rb_ary_new();
3770 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3771 rb_ary_push(aval, INT2NUM(exlist[i]));
3772 rb_ary_push(retval, aval);
3776 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3777 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3780 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3781 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3784 static VALUE s_AtomRef_GetHidden(VALUE self) {
3785 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3788 static VALUE s_AtomRef_GetAnchorList(VALUE self) {
3791 Atom *ap = s_AtomFromValue(self);
3792 if (ap->anchor == NULL)
3794 count = ap->anchor->connect.count;
3795 retval = rb_ary_new2(count * 2);
3796 cp = AtomConnectData(&ap->anchor->connect);
3797 for (i = 0; i < count; i++) {
3798 rb_ary_store(retval, i, INT2NUM(cp[i]));
3799 rb_ary_store(retval, i + count, rb_float_new(ap->anchor->coeffs[i]));
3804 static VALUE s_AtomRef_GetUFFType(VALUE self) {
3805 char *p = s_AtomFromValue(self)->uff_type;
3806 return rb_str_new(p, strlen_limit(p, 5));
3809 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3810 rb_raise(rb_eMolbyError, "index cannot be directly set");
3814 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3815 VALUE oval = s_AtomRef_GetSegSeq(self);
3816 val = rb_Integer(val);
3817 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3818 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3822 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3823 char *p = StringValuePtr(val);
3824 VALUE oval = s_AtomRef_GetSegName(self);
3825 strncpy(s_AtomFromValue(self)->segName, p, 4);
3826 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3830 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3831 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3832 return val; /* Not reached */
3835 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3836 Atom *ap = s_AtomFromValue(self);
3837 char *p = StringValuePtr(val);
3838 VALUE oval = s_AtomRef_GetName(self);
3839 if (ap->anchor != NULL && p[0] == '_')
3840 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
3841 strncpy(ap->aname, p, 4);
3842 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3846 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3848 char *p = StringValuePtr(val);
3849 VALUE oval = s_AtomRef_GetAtomType(self);
3850 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3851 if (type != 0 && type < kAtomTypeMinimum)
3852 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3853 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3854 mp->needsMDRebuild = 1;
3855 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3859 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3861 VALUE oval = s_AtomRef_GetCharge(self);
3862 val = rb_Float(val);
3863 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3864 mp->needsMDRebuild = 1;
3865 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3869 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3871 VALUE oval = s_AtomRef_GetWeight(self);
3872 val = rb_Float(val);
3873 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3874 mp->needsMDRebuild = 1;
3875 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3879 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3882 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3883 char *p = StringValuePtr(val);
3884 VALUE oval = s_AtomRef_GetElement(self);
3885 ap->atomicNumber = ElementToInt(p);
3886 ElementToString(ap->atomicNumber, ap->element);
3887 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3889 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3890 mp->needsMDRebuild = 1;
3894 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3897 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3898 VALUE oval = s_AtomRef_GetAtomicNumber(self);
3899 val = rb_Integer(val);
3900 ap->atomicNumber = NUM2INT(val);
3901 ElementToString(ap->atomicNumber, ap->element);
3902 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3904 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3905 mp->needsMDRebuild = 1;
3909 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3910 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3911 return val; /* Not reached */
3914 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3917 VALUE oval = s_AtomRef_GetR(self);
3918 VectorFromValue(val, &v);
3919 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3920 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3921 mp->needsMDCopyCoordinates = 1;
3925 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3928 VALUE oval = s_AtomRef_GetX(self);
3929 val = rb_Float(val);
3931 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3932 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3933 mp->needsMDCopyCoordinates = 1;
3937 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3940 VALUE oval = s_AtomRef_GetY(self);
3941 val = rb_Float(val);
3943 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3944 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3945 mp->needsMDCopyCoordinates = 1;
3949 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3952 VALUE oval = s_AtomRef_GetZ(self);
3953 val = rb_Float(val);
3955 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3956 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3957 mp->needsMDCopyCoordinates = 1;
3961 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3965 s_AtomIndexFromValue(self, &ap, &mp);
3967 VectorFromValue(val, &v);
3968 if (mp->cell != NULL)
3969 TransformVec(&v, mp->cell->tr, &v);
3971 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3972 mp->needsMDCopyCoordinates = 1;
3976 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3981 s_AtomIndexFromValue(self, &ap, &mp);
3983 val = rb_Float(val);
3985 if (mp->cell != NULL) {
3986 TransformVec(&v, mp->cell->rtr, &v);
3988 TransformVec(&v, mp->cell->tr, &v);
3991 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3992 mp->needsMDCopyCoordinates = 1;
3996 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
4001 s_AtomIndexFromValue(self, &ap, &mp);
4003 val = rb_Float(val);
4005 if (mp->cell != NULL) {
4006 TransformVec(&v, mp->cell->rtr, &v);
4008 TransformVec(&v, mp->cell->tr, &v);
4011 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4012 mp->needsMDCopyCoordinates = 1;
4016 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
4021 s_AtomIndexFromValue(self, &ap, &mp);
4023 val = rb_Float(val);
4025 if (mp->cell != NULL) {
4026 TransformVec(&v, mp->cell->rtr, &v);
4028 TransformVec(&v, mp->cell->tr, &v);
4031 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
4032 mp->needsMDCopyCoordinates = 1;
4036 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
4039 VALUE oval = s_AtomRef_GetSigma(self);
4040 VectorFromValue(val, &v);
4041 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
4042 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
4043 mp->needsMDCopyCoordinates = 1;
4047 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
4050 VALUE oval = s_AtomRef_GetSigmaX(self);
4051 val = rb_Float(val);
4053 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
4054 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
4055 mp->needsMDCopyCoordinates = 1;
4059 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
4062 VALUE oval = s_AtomRef_GetSigmaY(self);
4063 val = rb_Float(val);
4065 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
4066 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
4067 mp->needsMDCopyCoordinates = 1;
4071 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
4074 VALUE oval = s_AtomRef_GetSigmaZ(self);
4075 val = rb_Float(val);
4077 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
4078 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
4079 mp->needsMDCopyCoordinates = 1;
4083 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
4087 VALUE oval = s_AtomRef_GetV(self);
4088 VectorFromValue(val, &v);
4089 s_AtomIndexFromValue(self, &ap, &mp);
4091 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
4092 mp->needsMDCopyCoordinates = 1;
4096 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
4099 VALUE oval = s_AtomRef_GetF(self);
4100 VectorFromValue(val, &v);
4101 VecScaleSelf(v, KCAL2INTERNAL);
4102 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
4103 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
4104 mp->needsMDCopyCoordinates = 1;
4108 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
4109 VALUE oval = s_AtomRef_GetOccupancy(self);
4111 val = rb_Float(val);
4112 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
4113 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
4114 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
4118 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
4119 VALUE oval = s_AtomRef_GetTempFactor(self);
4120 val = rb_Float(val);
4121 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
4122 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
4126 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
4131 VALUE oval = s_AtomRef_GetAniso(self);
4132 Data_Get_Struct(self, AtomRef, aref);
4133 val = rb_funcall(val, rb_intern("to_a"), 0);
4134 n = RARRAY_LEN(val);
4135 valp = RARRAY_PTR(val);
4136 for (i = 0; i < 6; i++) {
4138 f[i] = NUM2DBL(rb_Float(valp[i]));
4142 type = NUM2INT(rb_Integer(valp[6]));
4145 for (i = 0; i < 6; i++)
4146 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
4148 for (i = 0; i < 6; i++)
4151 i = s_AtomIndexFromValue(self, NULL, NULL);
4152 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
4153 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
4157 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
4163 VALUE oval = s_AtomRef_GetSymop(self);
4164 i = s_AtomIndexFromValue(self, &ap, &mol);
4166 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
4168 val = rb_funcall(val, rb_intern("to_a"), 0);
4169 n = RARRAY_LEN(val);
4170 valp = RARRAY_PTR(val);
4171 for (i = 0; i < 5; i++) {
4173 if (valp[i] == Qnil)
4176 ival[i] = NUM2INT(rb_Integer(valp[i]));
4177 } else ival[i] = -100000;
4180 if (ival[0] != -100000 && (ival[0] < 0 || (ival[0] != 0 && ival[0] >= mol->nsyms)))
4181 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));
4182 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
4183 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
4184 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
4185 return val; /* No need to change */
4186 if (ival[0] != -100000)
4187 ap->symop.sym = ival[0];
4188 if (ival[1] != -100000)
4189 ap->symop.dx = ival[1];
4190 if (ival[2] != -100000)
4191 ap->symop.dy = ival[2];
4192 if (ival[3] != -100000)
4193 ap->symop.dz = ival[3];
4194 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
4195 if (ival[4] != -100000)
4196 ap->symbase = ival[4];
4197 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
4198 /* The anisotropic parameters should be recalculated */
4199 VALUE oaval = s_AtomRef_GetAniso(self);
4200 MoleculeSetAnisoBySymop(mol, i);
4201 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
4203 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
4207 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
4208 VALUE oval = s_AtomRef_GetIntCharge(self);
4209 val = rb_Integer(val);
4210 s_AtomFromValue(self)->intCharge = NUM2INT(val);
4211 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
4215 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
4217 VALUE oval = s_AtomRef_GetFixForce(self);
4218 val = rb_Float(val);
4219 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
4220 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
4221 mp->needsMDRebuild = 1;
4225 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
4228 VALUE oval = s_AtomRef_GetFixPos(self);
4229 VectorFromValue(val, &v);
4230 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
4231 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
4232 mp->needsMDRebuild = 1;
4236 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
4237 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
4238 return val; /* Not reached */
4241 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
4242 VALUE oval = s_AtomRef_GetIntCharge(self);
4243 val = rb_Integer(val);
4244 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
4245 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
4249 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
4250 VALUE oval = s_AtomRef_GetIntCharge(self);
4251 val = rb_Integer(val);
4252 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
4253 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
4257 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
4258 Atom *ap = s_AtomFromValue(self);
4259 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
4261 ap->exflags |= kAtomHiddenFlag;
4263 ap->exflags &= ~kAtomHiddenFlag;
4265 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
4269 static VALUE s_AtomRef_SetAnchorList(VALUE self, VALUE val) {
4270 Int idx, i, j, k, n, *ip;
4277 MolAction **undoActions;
4278 memset(&ac, 0, sizeof(ac));
4279 idx = s_AtomIndexFromValue(self, &ap, &mol);
4280 oval = s_AtomRef_GetAnchorList(self);
4282 val = rb_ary_to_ary(val);
4283 n = RARRAY_LEN(val);
4286 if (ap->anchor != NULL) {
4287 AtomConnectResize(&ap->anchor->connect, 0);
4288 free(ap->anchor->coeffs);
4291 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4296 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4297 if (ap->aname[0] == '_')
4298 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
4299 ip = (Int *)malloc(sizeof(Int) * n);
4301 for (i = 0; i < n; i++) {
4302 v = RARRAY_PTR(val)[i];
4303 if (rb_obj_is_kind_of(v, rb_cFloat))
4305 j = NUM2INT(rb_Integer(v));
4306 if (j < 0 || j >= mol->natoms)
4307 rb_raise(rb_eMolbyError, "set_anchor_list: atom index (%d) out of range", j);
4308 for (k = 0; k < i; k++) {
4310 rb_raise(rb_eMolbyError, "set_anchor_list: duplicate atom index (%d)", j);
4316 rb_raise(rb_eMolbyError, "set_anchor_list: the argument should have at least two atom indices");
4317 else if (i * 2 != n)
4318 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be given in the same number as the atom indices");
4319 dp = (Double *)malloc(sizeof(Double) * n / 2);
4320 for (i = 0; i < n / 2; i++) {
4321 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(val)[i + n / 2]));
4323 rb_raise(rb_eMolbyError, "set_anchor_list: the weights should be positive Floats");
4329 i = MoleculeSetPiAnchorList(mol, idx, n, ip, dp, &nUndoActions, &undoActions);
4333 rb_raise(rb_eMolbyError, "invalid argument");
4334 if (nUndoActions > 0) {
4335 for (i = 0; i < nUndoActions; i++) {
4336 MolActionCallback_registerUndo(mol, undoActions[i]);
4337 MolActionRelease(undoActions[i]);
4341 s_RegisterUndoForAtomAttrChange(self, s_AnchorListSym, val, oval);
4345 static VALUE s_AtomRef_SetUFFType(VALUE self, VALUE val) {
4346 Atom *ap = s_AtomFromValue(self);
4347 char *p = StringValuePtr(val);
4348 VALUE oval = s_AtomRef_GetUFFType(self);
4349 strncpy(ap->uff_type, p, 5);
4350 ap->uff_type[5] = 0;
4351 s_RegisterUndoForAtomAttrChange(self, s_UFFTypeSym, val, oval);
4355 static struct s_AtomAttrDef {
4357 VALUE *symref; /* Address of s_IndexSymbol etc. */
4358 ID id; /* Will be set within InitMolby() */
4359 VALUE (*getter)(VALUE);
4360 VALUE (*setter)(VALUE, VALUE);
4361 } s_AtomAttrDefTable[] = {
4362 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
4363 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
4364 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
4365 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
4366 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
4367 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
4368 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
4369 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
4370 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
4371 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
4372 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
4373 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
4374 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
4375 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
4376 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
4377 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4378 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4379 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4380 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4381 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4382 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4383 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4384 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4385 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4386 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4387 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4388 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4389 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4390 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4391 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4392 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4393 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4394 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4395 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4396 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4397 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4398 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4399 {"anchor_list", &s_AnchorListSym, 0, s_AtomRef_GetAnchorList, s_AtomRef_SetAnchorList},
4400 {"uff_type", &s_UFFTypeSym, 0, s_AtomRef_GetUFFType, s_AtomRef_SetUFFType},
4401 {NULL} /* Sentinel */
4405 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4409 if (TYPE(key) != T_SYMBOL) {
4410 kid = rb_intern(StringValuePtr(key));
4412 } else kid = SYM2ID(key);
4413 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4414 if (s_AtomAttrDefTable[i].id == kid) {
4415 if (value == Qundef)
4416 return (*(s_AtomAttrDefTable[i].getter))(self);
4418 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4421 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4422 return Qnil; /* not reached */
4426 s_AtomRef_GetAttr(VALUE self, VALUE key)
4428 return s_AtomRef_SetAttr(self, key, Qundef);
4433 * self == atomRef -> boolean
4435 * True if the two references point to the same atom.
4438 s_AtomRef_Equal(VALUE self, VALUE val)
4440 if (rb_obj_is_kind_of(val, rb_cAtomRef)) {
4441 return (s_AtomFromValue(self) == s_AtomFromValue(val) ? Qtrue : Qfalse);
4442 } else return Qfalse;
4445 #pragma mark ====== MolEnumerable Class ======
4447 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4451 * self[idx] -> AtomRef or Array of Integers
4453 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4454 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4455 * value is a String. Otherwise, the return value is an Array of Integers.
4458 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4460 MolEnumerable *mseq;
4463 Data_Get_Struct(self, MolEnumerable, mseq);
4465 if (mseq->kind == kAtomKind) {
4466 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4468 idx1 = NUM2INT(arg1);
4469 switch (mseq->kind) {
4471 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4472 if (idx2 < 0 || idx2 >= mol->nbonds)
4473 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4474 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4477 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4478 if (idx2 < 0 || idx2 >= mol->nangles)
4479 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4480 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4482 case kDihedralKind: {
4483 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4484 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4485 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4486 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]));
4488 case kImproperKind: {
4489 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4490 if (idx2 < 0 || idx2 >= mol->nimpropers)
4491 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4492 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]));
4494 case kResidueKind: {
4496 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4497 if (idx2 < 0 || idx2 >= mol->nresidues)
4498 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4499 p = mol->residues[idx2];
4500 return rb_str_new(p, strlen_limit(p, 4));
4510 * Returns the number of objects included in this enumerable.
4513 s_MolEnumerable_Length(VALUE self)
4515 MolEnumerable *mseq;
4516 Data_Get_Struct(self, MolEnumerable, mseq);
4517 switch (mseq->kind) {
4519 return INT2NUM(mseq->mol->natoms);
4521 return INT2NUM(mseq->mol->nbonds);
4523 return INT2NUM(mseq->mol->nangles);
4525 return INT2NUM(mseq->mol->ndihedrals);
4527 return INT2NUM(mseq->mol->nimpropers);
4529 return INT2NUM(mseq->mol->nresidues);
4538 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4539 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4540 * For the atoms, a same AtomRef object is passed (with different internal information)
4541 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4542 * for each iteration.
4545 s_MolEnumerable_Each(VALUE self)
4547 MolEnumerable *mseq;
4549 int len = NUM2INT(s_MolEnumerable_Length(self));
4550 Data_Get_Struct(self, MolEnumerable, mseq);
4551 if (mseq->kind == kAtomKind) {
4552 /* The same AtomRef object will be used during the loop */
4553 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4554 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4555 for (i = 0; i < len; i++) {
4560 /* A new ruby object will be created at each iteration (not very efficient) */
4561 for (i = 0; i < len; i++) {
4562 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4570 * self == molEnumerable -> boolean
4572 * True if the two arguments point to the same molecule and enumerable type.
4575 s_MolEnumerable_Equal(VALUE self, VALUE val)
4577 if (rb_obj_is_kind_of(val, rb_cMolEnumerable)) {
4578 MolEnumerable *mseq1, *mseq2;
4579 Data_Get_Struct(self, MolEnumerable, mseq1);
4580 Data_Get_Struct(val, MolEnumerable, mseq2);
4581 return ((mseq1->mol == mseq2->mol && mseq1->kind == mseq2->kind) ? Qtrue : Qfalse);
4582 } else return Qfalse;
4586 #pragma mark ====== Molecule Class ======
4588 /* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
4589 /* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
4590 char *gLoadSaveErrorMessage = NULL;
4592 #define MoleculeClearLoadSaveErrorMessage() (gLoadSaveErrorMessage != NULL ? (free(gLoadSaveErrorMessage), gLoadSaveErrorMessage = NULL) : NULL)
4595 MoleculeFromValue(VALUE val)
4598 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4599 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4600 Data_Get_Struct(val, Molecule, mol);
4604 static VALUE sMoleculeRetainArray = Qnil;
4606 /* The function is called from MoleculeRelease() */
4607 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4608 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4609 /* object is always returned for the same Molecule. */
4610 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4611 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4612 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4613 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4614 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4616 /* Register/unregister the exmolobj Ruby object */
4618 MoleculeReleaseExternalObj(Molecule *mol)
4620 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4621 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4622 mol->exmolobjProtected = 0;
4627 MoleculeRetainExternalObj(Molecule *mol)
4629 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4630 if (sMoleculeRetainArray == Qnil) {
4631 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4632 sMoleculeRetainArray = rb_ary_new();
4635 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4636 mol->exmolobjProtected = 1;
4640 /* Release hook function for Ruby */
4642 MoleculeReleaseHook(Molecule *mol)
4644 if (mol->exmolobj != NULL) {
4645 /* No need to remove from sMoleculeRetainArray */
4646 mol->exmolobj = NULL;
4647 mol->exmolobjProtected = 0;
4649 MoleculeRelease(mol);
4653 ValueFromMolecule(Molecule *mol)
4657 if (mol->exmolobj != NULL)
4658 return (VALUE)mol->exmolobj;
4659 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4660 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4661 return (VALUE)mol->exmolobj;
4666 s_Molecule_Alloc(VALUE klass)
4669 Molecule *mol = MoleculeNew();
4670 val = ValueFromMolecule(mol);
4671 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4676 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4680 if (FIXNUM_P(val)) {
4682 if (n >= 0 && n < mol->natoms)
4684 n = -1; /* No such atom */
4685 val = rb_inspect(val);
4687 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4689 if (n >= 0 && n < mol->natoms)
4691 p = StringValuePtr(val);
4693 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4695 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4697 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4698 return 0; /* Not reached */
4702 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4705 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4706 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4707 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4708 Data_Get_Struct(val, IntGroup, ig);
4714 s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
4716 if (gLoadSaveErrorMessage != NULL) {
4717 MyAppCallback_setConsoleColor(1);
4718 MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
4719 MyAppCallback_setConsoleColor(0);
4722 rb_raise(rb_eMolbyError, "%s %s", msg, fname);
4729 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4730 * created object does not affect the old object in any sense.
4733 s_Molecule_InitCopy(VALUE self, VALUE arg)
4735 Molecule *mp1, *mp2;
4736 Data_Get_Struct(self, Molecule, mp1);
4737 mp2 = MoleculeFromValue(arg);
4738 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4739 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4745 * loadmbsf(file) -> bool
4747 * Read a structure from a mbsf file.
4748 * Return true if successful.
4751 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4757 MoleculeClearLoadSaveErrorMessage();
4758 Data_Get_Struct(self, Molecule, mol);
4759 rb_scan_args(argc, argv, "1", &fname);
4760 fstr = FileStringValuePtr(fname);
4761 retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4762 s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
4768 * loadpsf(file, pdbfile = nil) -> bool
4770 * Read a structure from a psf file. molecule must be empty. The psf may be
4771 * an "extended" version, which also contains coordinates. If pdbfile
4772 * is given, then atomic coordinates are read from that file.
4773 * Return true if successful.
4776 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4778 VALUE fname, pdbname;
4779 char *fstr, *pdbstr;
4782 Data_Get_Struct(self, Molecule, mol);
4783 if (mol->natoms > 0)
4784 return Qnil; /* Must be a new molecule */
4785 MoleculeClearLoadSaveErrorMessage();
4786 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4787 fstr = FileStringValuePtr(fname);
4788 retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4789 s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
4791 if (!NIL_P(pdbname)) {
4792 pdbstr = strdup(FileStringValuePtr(pdbname));
4793 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
4795 s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
4802 * loadpdb(file) -> bool
4804 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4805 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4806 * Return true if successful.
4809 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4815 Data_Get_Struct(self, Molecule, mol);
4816 rb_scan_args(argc, argv, "1", &fname);
4817 MoleculeClearLoadSaveErrorMessage();
4818 fstr = FileStringValuePtr(fname);
4819 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4820 s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
4826 * loaddcd(file) -> bool
4828 * Read coordinates from a dcd file. The molecule should not empty.
4829 * Return true if successful.
4832 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4838 Data_Get_Struct(self, Molecule, mol);
4839 rb_scan_args(argc, argv, "1", &fname);
4840 MoleculeClearLoadSaveErrorMessage();
4841 fstr = FileStringValuePtr(fname);
4842 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
4843 s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
4849 * loadtep(file) -> bool
4851 * Read coordinates from an ortep .tep file.
4852 * Return true if successful.
4855 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4861 Data_Get_Struct(self, Molecule, mol);
4862 rb_scan_args(argc, argv, "1", &fname);
4863 MoleculeClearLoadSaveErrorMessage();
4864 fstr = FileStringValuePtr(fname);
4865 retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
4866 s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
4872 * loadres(file) -> bool
4874 * Read coordinates from a shelx .res file.
4875 * Return true if successful.
4878 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4884 Data_Get_Struct(self, Molecule, mol);
4885 rb_scan_args(argc, argv, "1", &fname);
4886 MoleculeClearLoadSaveErrorMessage();
4887 fstr = FileStringValuePtr(fname);
4888 retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
4889 s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
4895 * loadfchk(file) -> bool
4897 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
4898 * Return true if successful.
4901 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4907 Data_Get_Struct(self, Molecule, mol);
4908 rb_scan_args(argc, argv, "1", &fname);
4909 MoleculeClearLoadSaveErrorMessage();
4910 fstr = FileStringValuePtr(fname);
4911 retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
4912 s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
4918 * loaddat(file) -> bool
4920 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
4921 * Return true if successful.
4924 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4930 Data_Get_Struct(self, Molecule, mol);
4931 rb_scan_args(argc, argv, "1", &fname);
4932 MoleculeClearLoadSaveErrorMessage();
4933 fstr = FileStringValuePtr(fname);
4934 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4935 retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
4936 MyAppCallback_hideProgressPanel();
4937 s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
4943 * savembsf(file) -> bool
4945 * Write structure as a mbsf file. Returns true if successful.
4948 s_Molecule_Savembsf(VALUE self, VALUE fname)
4953 Data_Get_Struct(self, Molecule, mol);
4954 MoleculeClearLoadSaveErrorMessage();
4955 fstr = FileStringValuePtr(fname);
4956 retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
4957 s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
4963 * savepsf(file) -> bool
4965 * Write structure as a psf file. Returns true if successful.
4968 s_Molecule_Savepsf(VALUE self, VALUE fname)
4973 Data_Get_Struct(self, Molecule, mol);
4974 MoleculeClearLoadSaveErrorMessage();
4975 fstr = FileStringValuePtr(fname);
4976 retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
4977 s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
4983 * savepdb(file) -> bool
4985 * Write coordinates as a pdb file. Returns true if successful.
4988 s_Molecule_Savepdb(VALUE self, VALUE fname)
4993 Data_Get_Struct(self, Molecule, mol);
4994 MoleculeClearLoadSaveErrorMessage();
4995 fstr = FileStringValuePtr(fname);
4996 retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
4997 s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
5003 * savedcd(file) -> bool
5005 * Write coordinates as a dcd file. Returns true if successful.
5008 s_Molecule_Savedcd(VALUE self, VALUE fname)
5013 Data_Get_Struct(self, Molecule, mol);
5014 MoleculeClearLoadSaveErrorMessage();
5015 fstr = FileStringValuePtr(fname);
5016 retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
5017 s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
5023 * savetep(file) -> bool
5025 * Write coordinates as an ORTEP file. Returns true if successful.
5029 s_Molecule_Savetep(VALUE self, VALUE fname)
5034 Data_Get_Struct(self, Molecule, mol);
5035 fstr = FileStringValuePtr(fname);
5036 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
5037 rb_raise(rb_eMolbyError, errbuf);
5042 /* load([ftype, ] fname, ...) */
5044 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
5047 char *argstr, *methname, *p, *type = "";
5050 const char *ls = (loadFlag ? "load" : "save");
5051 int lslen = strlen(ls);
5056 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
5057 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
5058 if (argstr[0] == ':') {
5059 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
5060 methname = ALLOC_N(char, lslen + strlen(argstr));
5061 strcpy(methname, ls);
5062 strcat(methname, argstr + 1);
5064 for (i = lslen; methname[i] != 0; i++)
5065 methname[i] = tolower(methname[i]);
5066 mid = rb_intern(methname);
5070 rval = rb_funcall2(self, mid, argc, argv);
5076 /* Guess file type from extension */
5077 p = strrchr(argstr, '.');
5081 for (methname = p; *methname != 0; methname++) {
5082 if (!isalpha(*methname))
5085 if (*methname == 0) {
5086 methname = ALLOC_N(char, lslen + strlen(p) + 1);
5087 if (methname == NULL)
5088 rb_raise(rb_eMolbyError, "Low memory");
5089 strcpy(methname, ls);
5090 strcat(methname, p);
5091 for (i = lslen; methname[i] != 0; i++)
5092 methname[i] = tolower(methname[i]);
5093 mid = rb_intern(methname);
5096 if (rb_respond_to(self, mid)) {
5097 /* Load: try to call the load procedure only if it is available */
5098 rval = rb_funcall2(self, mid, argc, argv);
5103 /* Save: call the save procedure, and if not found then call 'method_missing' */
5104 rval = rb_funcall2(self, mid, argc, argv);
5111 rval = rb_str_to_str(argv[0]);
5112 asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
5113 s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
5114 return Qnil; /* Does not reach here */
5118 /* Register the path */
5121 Data_Get_Struct(self, Molecule, mol);
5122 MoleculeSetPath(mol, StringValuePtr(argv[0]));
5124 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
5125 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5126 if (ap->occupancy != 0.0)
5129 if (i == mol->natoms) {
5130 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
5131 ap->occupancy = 1.0;
5140 * molload(file, *args) -> bool
5142 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
5143 * file type given by the extension). If this method fails, then all defined (public)
5144 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
5147 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
5149 return s_Molecule_LoadSave(argc, argv, self, 1);
5154 * molsave(file, *args) -> bool
5156 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
5157 * (XXX is the file type given by the extension).
5160 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
5162 return s_Molecule_LoadSave(argc, argv, self, 0);
5169 * Returns the display name of the molecule. If the molecule has no associated
5170 * document, then returns nil.
5173 s_Molecule_Name(VALUE self)
5177 Data_Get_Struct(self, Molecule, mol);
5178 MoleculeCallback_displayName(mol, buf, sizeof buf);
5182 return rb_str_new2(buf);
5187 * set_name(string) -> self
5189 * Set the name of an untitled molecule. If the molecule is not associated with window
5190 * or it already has an associated file, then exception is thrown.
5193 s_Molecule_SetName(VALUE self, VALUE nval)
5196 Data_Get_Struct(self, Molecule, mol);
5197 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
5198 rb_raise(rb_eMolbyError, "Cannot change the window title");
5207 * Returns the full path name of the molecule, if it is associated with a file.
5208 * If the molecule has no associated file, then returns nil.
5211 s_Molecule_Path(VALUE self)
5215 Data_Get_Struct(self, Molecule, mol);
5216 MoleculeCallback_pathName(mol, buf, sizeof buf);
5220 return Ruby_NewFileStringValue(buf);
5227 * Returns the full path name of the directory in which the file associated with the
5228 * molecule is located. If the molecule has no associated file, then returns nil.
5231 s_Molecule_Dir(VALUE self)
5235 Data_Get_Struct(self, Molecule, mol);
5236 MoleculeCallback_pathName(mol, buf, sizeof buf);
5238 translate_char(buf, '\\', '/');
5243 p = strrchr(buf, '/');
5246 return rb_str_new2(buf);
5254 * Returns a string in the form "Molecule[name]" if the molecule has the associated
5255 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
5256 * the Molecule structure) is returned.
5259 s_Molecule_Inspect(VALUE self)
5263 Data_Get_Struct(self, Molecule, mol);
5264 MoleculeCallback_displayName(mol, buf, sizeof buf);
5266 /* No associated document */
5267 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
5268 return rb_str_new2(buf);
5270 /* Check whether the document name is duplicate */
5274 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
5275 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
5276 if (strcmp(buf, buf2) == 0) {
5283 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
5285 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
5287 return rb_str_new2(buf2);
5294 * open(file) -> Molecule
5296 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
5299 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
5305 rb_scan_args(argc, argv, "01", &fname);
5309 p = FileStringValuePtr(fname);
5310 iflag = Ruby_SetInterruptFlag(Qfalse);
5311 mp = MoleculeCallback_openNewMolecule(p);
5312 Ruby_SetInterruptFlag(iflag);
5315 rb_raise(rb_eMolbyError, "Cannot create untitled document");
5317 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
5319 return ValueFromMolecule(mp);
5325 * new(file, *args) -> Molecule
5327 * Create a new molecule and call "load" method with the same arguments.
5330 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
5333 return s_Molecule_Load(argc, argv, self);
5334 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
5338 s_Molecule_MolEnumerable(VALUE self, int kind)
5341 MolEnumerable *mseq;
5342 Data_Get_Struct(self, Molecule, mol);
5343 mseq = MolEnumerableNew(mol, kind);
5344 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
5349 * atoms -> MolEnumerable
5351 * Returns a MolEnumerable object representing the array of atoms.
5354 s_Molecule_Atoms(VALUE self)
5356 return s_Molecule_MolEnumerable(self, kAtomKind);
5361 * bonds -> MolEnumerable
5363 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
5364 * by an array of two atom indices.
5367 s_Molecule_Bonds(VALUE self)
5369 return s_Molecule_MolEnumerable(self, kBondKind);
5374 * angles -> MolEnumerable
5376 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5377 * by an array of three atom indices.
5380 s_Molecule_Angles(VALUE self)
5382 return s_Molecule_MolEnumerable(self, kAngleKind);
5387 * dihedrals -> MolEnumerable
5389 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5390 * by an array of four atom indices.
5393 s_Molecule_Dihedrals(VALUE self)
5395 return s_Molecule_MolEnumerable(self, kDihedralKind);
5400 * impropers -> MolEnumerable
5402 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5403 * by an array of four atom indices.
5406 s_Molecule_Impropers(VALUE self)
5408 return s_Molecule_MolEnumerable(self, kImproperKind);
5413 * residues -> MolEnumerable
5415 * Returns a MolEnumerable object representing the array of residue names.
5418 s_Molecule_Residues(VALUE self)
5420 return s_Molecule_MolEnumerable(self, kResidueKind);
5427 * Returns the number of atoms.
5430 s_Molecule_Natoms(VALUE self)
5433 Data_Get_Struct(self, Molecule, mol);
5434 return INT2NUM(mol->natoms);
5441 * Returns the number of bonds.
5444 s_Molecule_Nbonds(VALUE self)
5447 Data_Get_Struct(self, Molecule, mol);
5448 return INT2NUM(mol->nbonds);
5453 * nangles -> Integer
5455 * Returns the number of angles.
5458 s_Molecule_Nangles(VALUE self)
5461 Data_Get_Struct(self, Molecule, mol);
5462 return INT2NUM(mol->nangles);
5467 * ndihedrals -> Integer
5469 * Returns the number of dihedrals.
5472 s_Molecule_Ndihedrals(VALUE self)
5475 Data_Get_Struct(self, Molecule, mol);
5476 return INT2NUM(mol->ndihedrals);
5481 * nimpropers -> Integer
5483 * Returns the number of impropers.
5486 s_Molecule_Nimpropers(VALUE self)
5489 Data_Get_Struct(self, Molecule, mol);
5490 return INT2NUM(mol->nimpropers);
5495 * nresidues -> Integer
5497 * Returns the number of residues.
5500 s_Molecule_Nresidues(VALUE self)
5503 Data_Get_Struct(self, Molecule, mol);
5504 return INT2NUM(mol->nresidues);
5508 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5510 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.");
5515 * bond_par(idx) -> ParameterRef
5517 * Returns the MD parameter for the idx-th bond.
5521 s_Molecule_BondPar(VALUE self, VALUE val)
5528 Data_Get_Struct(self, Molecule, mol);
5529 ival = NUM2INT(rb_Integer(val));
5530 if (ival < -mol->nbonds || ival >= mol->nbonds)
5531 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5533 ival += mol->nbonds;
5534 s_RebuildMDParameterIfNecessary(self, Qtrue);
5535 i1 = mol->bonds[ival * 2];
5536 i2 = mol->bonds[ival * 2 + 1];
5537 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5538 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5539 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5542 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5548 * angle_par(idx) -> ParameterRef
5550 * Returns the MD parameter for the idx-th angle.
5554 s_Molecule_AnglePar(VALUE self, VALUE val)
5561 Data_Get_Struct(self, Molecule, mol);
5562 ival = NUM2INT(rb_Integer(val));
5563 if (ival < -mol->nangles || ival >= mol->nangles)
5564 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5566 ival += mol->nangles;
5567 s_RebuildMDParameterIfNecessary(self, Qtrue);
5568 i1 = mol->angles[ival * 3];
5569 i2 = mol->angles[ival * 3 + 1];
5570 i3 = mol->angles[ival * 3 + 2];
5571 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5572 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5573 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5574 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5577 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5582 * dihedral_par(idx) -> ParameterRef
5584 * Returns the MD parameter for the idx-th dihedral.
5588 s_Molecule_DihedralPar(VALUE self, VALUE val)
5593 UInt t1, t2, t3, t4;
5595 Data_Get_Struct(self, Molecule, mol);
5596 ival = NUM2INT(rb_Integer(val));
5597 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5598 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5600 ival += mol->ndihedrals;
5601 s_RebuildMDParameterIfNecessary(self, Qtrue);
5602 i1 = mol->dihedrals[ival * 4];
5603 i2 = mol->dihedrals[ival * 4 + 1];
5604 i3 = mol->dihedrals[ival * 4 + 2];
5605 i4 = mol->dihedrals[ival * 4 + 3];
5606 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5607 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5608 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5609 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5610 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5613 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5618 * improper_par(idx) -> ParameterRef
5620 * Returns the MD parameter for the idx-th improper.
5624 s_Molecule_ImproperPar(VALUE self, VALUE val)
5629 UInt t1, t2, t3, t4;
5631 Data_Get_Struct(self, Molecule, mol);
5632 ival = NUM2INT(rb_Integer(val));
5633 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5634 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5636 ival += mol->nimpropers;
5637 s_RebuildMDParameterIfNecessary(self, Qtrue);
5638 i1 = mol->impropers[ival * 4];
5639 i2 = mol->impropers[ival * 4 + 1];
5640 i3 = mol->impropers[ival * 4 + 2];
5641 i4 = mol->impropers[ival * 4 + 3];
5642 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5643 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5644 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5645 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5646 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5649 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5655 * start_step -> Integer
5657 * Returns the start step (defined by dcd format).
5660 s_Molecule_StartStep(VALUE self)
5663 Data_Get_Struct(self, Molecule, mol);
5664 return INT2NUM(mol->startStep);
5669 * start_step = Integer
5671 * Set the start step (defined by dcd format).
5674 s_Molecule_SetStartStep(VALUE self, VALUE val)
5677 Data_Get_Struct(self, Molecule, mol);
5678 mol->startStep = NUM2INT(rb_Integer(val));
5684 * steps_per_frame -> Integer
5686 * Returns the number of steps between frames (defined by dcd format).
5689 s_Molecule_StepsPerFrame(VALUE self)
5692 Data_Get_Struct(self, Molecule, mol);
5693 return INT2NUM(mol->stepsPerFrame);
5698 * steps_per_frame = Integer
5700 * Set the number of steps between frames (defined by dcd format).
5703 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5706 Data_Get_Struct(self, Molecule, mol);
5707 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5713 * ps_per_step -> Float
5715 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5718 s_Molecule_PsPerStep(VALUE self)
5721 Data_Get_Struct(self, Molecule, mol);
5722 return rb_float_new(mol->psPerStep);
5727 * ps_per_step = Float
5729 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5732 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5735 Data_Get_Struct(self, Molecule, mol);
5736 mol->psPerStep = NUM2DBL(rb_Float(val));
5742 * find_angles -> Integer
5744 * Find the angles from the bonds. Returns the number of angles newly created.
5748 s_Molecule_FindAngles(VALUE self)
5754 Data_Get_Struct(self, Molecule, mol);
5755 if (mol == NULL || mol->natoms == 0)
5759 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5760 nc = ap->connect.count;
5762 for (i = 0; i < nc; i++) {
5763 n[0] = ap->connects[i];
5764 for (j = i + 1; j < nc; j++) {
5765 n[2] = ap->connects[j];
5766 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5767 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5772 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5775 return INT2NUM(nip);
5780 * find_dihedrals -> Integer
5782 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5786 s_Molecule_FindDihedrals(VALUE self)
5790 int n1, i, j, k, nc1, nc2;
5792 Data_Get_Struct(self, Molecule, mol);
5793 if (mol == NULL || mol->natoms == 0)
5797 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5798 nc1 = ap1->connect.count;
5800 for (i = 0; i < nc1; i++) {
5801 n[2] = ap1->connects[i];
5804 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5805 nc2 = ap2->connect.count;
5806 for (j = 0; j < nc1; j++) {
5807 n[0] = ap1->connects[j];
5810 for (k = 0; k < nc2; k++) {
5811 n[3] = ap2->connects[k];
5812 if (n[3] == n1 || n[3] == n[0])
5814 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5815 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5821 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5824 return INT2NUM(nip);
5830 * nresidues = Integer
5832 * Change the number of residues.
5835 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5838 int ival = NUM2INT(val);
5839 Data_Get_Struct(self, Molecule, mol);
5840 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5841 if (ival != mol->nresidues)
5842 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5848 * max_residue_number(atom_group = nil) -> Integer
5850 * Returns the maximum residue number actually used. If an atom group is given, only
5851 * these atoms are examined. If no atom is present, nil is returned.
5854 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5860 Data_Get_Struct(self, Molecule, mol);
5861 rb_scan_args(argc, argv, "01", &gval);
5862 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5863 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5864 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5869 * min_residue_number(atom_group = nil) -> Integer
5871 * Returns the minimum residue number actually used. If an atom group is given, only
5872 * these atoms are examined. If no atom is present, nil is returned.
5875 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5881 Data_Get_Struct(self, Molecule, mol);
5882 rb_scan_args(argc, argv, "01", &gval);
5883 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5884 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5885 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5890 * each_atom(atom_group = nil) {|aref| ...}
5892 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5893 * group is given, only these atoms are processed.
5894 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5895 * is self (a Molecule object).
5898 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5906 Data_Get_Struct(self, Molecule, mol);
5907 rb_scan_args(argc, argv, "01", &gval);
5908 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5909 arval = ValueFromMoleculeAndIndex(mol, 0);
5910 Data_Get_Struct(arval, AtomRef, aref);
5911 for (i = 0; i < mol->natoms; i++) {
5913 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5917 IntGroupRelease(ig);
5923 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5925 * Returns the unit cell parameters. If cell is not set, returns nil.
5928 s_Molecule_Cell(VALUE self)
5933 Data_Get_Struct(self, Molecule, mol);
5934 if (mol->cell == NULL)
5936 val = rb_ary_new2(6);
5937 for (i = 0; i < 6; i++)
5938 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5939 if (mol->cell->has_sigma) {
5940 for (i = 0; i < 6; i++) {
5941 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5949 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5950 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
5952 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5953 If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
5954 This operation is undoable.
5955 Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
5958 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5962 int i, convert_coord, n;
5964 Data_Get_Struct(self, Molecule, mol);
5965 rb_scan_args(argc, argv, "11", &val, &cval);
5970 val = rb_ary_to_ary(val);
5971 len = RARRAY_LEN(val);
5974 } else if (len >= 6) {
5976 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5977 for (i = 0; i < n; i++)
5978 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5980 convert_coord = (RTEST(cval) ? 1 : 0);
5981 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
5987 * box -> [avec, bvec, cvec, origin, flags]
5989 * Get the unit cell information in the form of a periodic bounding box.
5990 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
5991 * Integers which define whether the system is periodic along the axis.
5992 * If no unit cell is defined, nil is returned.
5995 s_Molecule_Box(VALUE self)
5999 Data_Get_Struct(self, Molecule, mol);
6000 if (mol == NULL || mol->cell == NULL)
6002 v[0] = ValueFromVector(&(mol->cell->axes[0]));
6003 v[1] = ValueFromVector(&(mol->cell->axes[1]));
6004 v[2] = ValueFromVector(&(mol->cell->axes[2]));
6005 v[3] = ValueFromVector(&(mol->cell->origin));
6006 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
6007 val = rb_ary_new4(5, v);
6013 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
6014 * set_box(d, origin = [0, 0, 0])
6017 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
6018 If it is a number, the x/y/z axis vector is multiplied with the given number and used
6020 Flags, if present, is a 3-member array of Integers defining whether the system is
6021 periodic along the axis.
6022 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
6023 In the second form, an isotropic box with cell-length d is set.
6024 In the third form, the existing box is cleared.
6025 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
6028 s_Molecule_SetBox(VALUE self, VALUE aval)
6032 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
6034 Vector origin = {0, 0, 0};
6037 int i, convertCoordinates = 0;
6038 Data_Get_Struct(self, Molecule, mol);
6040 MolActionCreateAndPerform(mol, gMolActionClearBox);
6043 aval = rb_ary_to_ary(aval);
6044 for (i = 0; i < 6; i++) {
6045 if (i < RARRAY_LEN(aval))
6046 v[i] = (RARRAY_PTR(aval))[i];
6050 MolActionCreateAndPerform(mol, gMolActionClearBox);
6053 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
6054 d = NUM2DBL(rb_Float(v[0]));
6055 for (i = 0; i < 3; i++)
6056 VecScale(vv[i], ax[i], d);
6058 VectorFromValue(v[1], &origin);
6059 flags[0] = flags[1] = flags[2] = 1;
6061 for (i = 0; i < 3; i++) {
6064 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
6065 d = NUM2DBL(rb_Float(v[i]));
6066 VecScale(vv[i], ax[i], d);
6068 VectorFromValue(v[i], &vv[i]);
6070 flags[i] = (VecLength2(vv[i]) > 0.0);
6073 VectorFromValue(v[3], &origin);
6075 for (i = 0; i < 3; i++) {
6076 VALUE val = Ruby_ObjectAtIndex(v[4], i);
6077 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
6081 convertCoordinates = 1;
6083 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
6089 * cell_periodicity -> [n1, n2, n3]
6091 * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
6095 s_Molecule_CellPeriodicity(VALUE self)
6098 Data_Get_Struct(self, Molecule, mol);
6099 if (mol->cell == NULL)
6101 return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
6106 * self.cell_periodicity = [n1, n2, n3] or Integer or nil
6107 * set_cell_periodicity = [n1, n2, n3] or Integer or nil
6109 * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
6110 * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
6111 * If cell is not defined, exception is raised.
6112 * This operation is undoable.
6115 s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
6119 Data_Get_Struct(self, Molecule, mol);
6120 if (mol->cell == NULL)
6121 rb_raise(rb_eMolbyError, "periodic cell is not defined");
6124 else if (rb_obj_is_kind_of(arg, rb_cNumeric))
6125 flag = NUM2INT(rb_Integer(arg));
6129 arg = rb_ary_to_ary(arg);
6131 for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
6132 arg0 = RARRAY_PTR(arg)[i];
6133 if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
6134 flag |= (1 << (2 - i));
6137 MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
6143 * cell_flexibility -> bool
6145 * Returns the unit cell is flexible or not
6148 s_Molecule_CellFlexibility(VALUE self)
6150 rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
6153 Data_Get_Struct(self, Molecule, mol);
6154 if (mol->cell == NULL)
6156 if (mol->useFlexibleCell)
6158 else return Qfalse; */
6163 * self.cell_flexibility = bool
6164 * set_cell_flexibility(bool)
6166 * Change the unit cell is flexible or not
6169 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
6171 rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
6174 Data_Get_Struct(self, Molecule, mol);
6175 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
6181 * cell_transform -> Transform
6183 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
6184 * If cell is not defined, nil is returned.
6187 s_Molecule_CellTransform(VALUE self)
6190 Data_Get_Struct(self, Molecule, mol);
6191 if (mol == NULL || mol->cell == NULL)
6193 return ValueFromTransform(&(mol->cell->tr));
6198 * symmetry -> Array of Transforms
6199 * symmetries -> Array of Transforms
6201 * Get the currently defined symmetry operations. If no symmetry operation is defined,
6202 * returns an empty array.
6205 s_Molecule_Symmetry(VALUE self)
6210 Data_Get_Struct(self, Molecule, mol);
6211 if (mol->nsyms <= 0)
6212 return rb_ary_new();
6213 val = rb_ary_new2(mol->nsyms);
6214 for (i = 0; i < mol->nsyms; i++) {
6215 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
6222 * nsymmetries -> Integer
6224 * Get the number of currently defined symmetry operations.
6227 s_Molecule_Nsymmetries(VALUE self)
6230 Data_Get_Struct(self, Molecule, mol);
6231 return INT2NUM(mol->nsyms);
6236 * add_symmetry(Transform) -> Integer
6238 * Add a new symmetry operation. If no symmetry operation is defined and the
6239 * given argument is not an identity transform, then also add an identity
6240 * transform at the index 0.
6241 * Returns the total number of symmetries after operation.
6244 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
6248 Data_Get_Struct(self, Molecule, mol);
6249 TransformFromValue(trans, &tr);
6250 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
6251 return INT2NUM(mol->nsyms);
6256 * remove_symmetry(count = nil) -> Integer
6257 * remove_symmetries(count = nil) -> Integer
6259 * Remove the specified number of symmetry operations. The last added ones are removed
6260 * first. If count is nil, then all symmetry operations are removed. Returns the
6261 * number of leftover symmetries.
6264 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
6269 Data_Get_Struct(self, Molecule, mol);
6270 rb_scan_args(argc, argv, "01", &cval);
6274 n = NUM2INT(rb_Integer(cval));
6275 if (n < 0 || n > mol->nsyms)
6276 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
6277 if (n == mol->nsyms)
6280 for (i = 0; i < n; i++)
6281 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
6282 return INT2NUM(mol->nsyms);
6286 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
6288 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
6289 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
6290 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
6291 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
6298 * atom_group {|aref| ...}
6299 * atom_group(arg1, arg2, ...)
6300 * atom_group(arg1, arg2, ...) {|aref| ...}
6302 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
6303 * If arguments are given, then the atoms reprensented by the arguments are added to the
6304 * group. For a conversion of a string to an atom index, see the description
6305 * of Molecule#atom_index.
6306 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
6307 * representing each atom, and the atoms are removed from the result if the block returns false.
6311 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
6313 IntGroup *ig1, *ig2;
6315 Int i, startPt, interval;
6316 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
6317 Data_Get_Struct(retval, IntGroup, ig1);
6318 Data_Get_Struct(self, Molecule, mol);
6320 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
6323 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
6324 i = s_Molecule_AtomIndexFromValue(mol, *argv);
6325 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
6326 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
6327 ig2 = IntGroupFromValue(*argv);
6328 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
6329 interval = IntGroupGetInterval(ig2, i);
6330 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
6332 IntGroupRelease(ig2);
6333 } else if (rb_respond_to(*argv, rb_intern("each"))) {
6335 values[0] = (VALUE)mol;
6336 values[1] = (VALUE)ig1;
6337 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
6339 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
6344 if (rb_block_given_p()) {
6345 /* Evaluate the given block with an AtomRef as the argument, and delete
6346 the index if the block returns false */
6347 AtomRef *aref = AtomRefNew(mol, 0);
6348 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6349 ig2 = IntGroupNew();
6350 IntGroupCopy(ig2, ig1);
6351 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
6353 if (startPt >= mol->natoms)
6355 aref->idx = startPt;
6356 resval = rb_yield(arval);
6358 IntGroupRemove(ig1, startPt, 1);
6360 IntGroupRelease(ig2);
6363 /* Remove points that are out of bounds */
6364 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
6371 * atom_index(val) -> Integer
6373 * Returns the atom index represented by val. val can be either a non-negative integer
6374 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
6375 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
6376 * where resname, resid, name are the residue name, residue id, and atom name respectively.
6377 * If val is a string and multiple atoms match the description, the atom with the lowest index
6381 s_Molecule_AtomIndex(VALUE self, VALUE val)
6384 Data_Get_Struct(self, Molecule, mol);
6385 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
6390 * extract(group, dummy_flag = nil) -> Molecule
6392 * Extract the atoms given by group and return as a new molecule object.
6393 * If dummy_flag is true, then the atoms that are not included in the group but are connected
6394 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
6395 * names beginning with an underscore) and included in the new molecule object.
6398 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
6400 Molecule *mol1, *mol2;
6402 VALUE group, dummy_flag, retval;
6403 Data_Get_Struct(self, Molecule, mol1);
6404 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
6405 ig = s_Molecule_AtomGroupFromValue(self, group);
6406 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
6409 retval = ValueFromMolecule(mol2);
6411 IntGroupRelease(ig);
6417 * add(molecule2) -> self
6419 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6421 This operation is undoable.
6424 s_Molecule_Add(VALUE self, VALUE val)
6426 Molecule *mol1, *mol2;
6427 Data_Get_Struct(self, Molecule, mol1);
6428 mol2 = MoleculeFromValue(val);
6429 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6435 * remove(group) -> Molecule
6437 * The atoms designated by the given group are removed from the molecule.
6438 * This operation is undoable.
6441 s_Molecule_Remove(VALUE self, VALUE group)
6446 IntGroupIterator iter;
6448 Data_Get_Struct(self, Molecule, mol1);
6449 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6450 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6451 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6452 Data_Get_Struct(group, IntGroup, ig);
6454 /* Remove the bonds between the two fragments */
6455 /* (This is necessary for undo to work correctly) */
6456 IntGroupIteratorInit(ig, &iter);
6458 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6459 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6461 cp = AtomConnectData(&ap->connect);
6462 for (j = 0; j < ap->connect.count; j++) {
6464 if (!IntGroupLookup(ig, n, NULL)) {
6465 /* bond i-n, i is in ig and n is not */
6466 int k = MoleculeLookupBond(mol1, i, n);
6470 IntGroupAdd(bg, k, 1);
6475 IntGroupIteratorRelease(&iter);
6478 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
6479 IntGroupRelease(bg);
6482 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6489 * create_atom(name, pos = -1) -> AtomRef
6491 * Create a new atom with the specified name (may contain residue
6492 * information) and position (if position is out of range, the atom is appended at
6493 * the end). Returns the reference to the new atom.
6494 * This operation is undoable.
6497 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6504 char *p, resName[6], atomName[6];
6506 Data_Get_Struct(self, Molecule, mol);
6507 rb_scan_args(argc, argv, "02", &name, &ival);
6509 pos = NUM2INT(rb_Integer(ival));
6512 p = StringValuePtr(name);
6514 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6515 if (atomName[0] == 0)
6516 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6519 if (p == NULL || p[0] == 0) {
6520 memset(atomName, 0, 4);
6523 memset(&arec, 0, sizeof(arec));
6524 strncpy(arec.aname, atomName, 4);
6526 strncpy(arec.resName, resName, 4);
6527 arec.resSeq = resSeq;
6529 arec.occupancy = 1.0;
6530 // i = MoleculeCreateAnAtom(mol, &arec);
6531 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6533 aref = AtomRefNew(mol, pos);
6534 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6539 * duplicate_atom(atomref, pos = -1) -> AtomRef
6541 * Create a new atom with the same attributes (but no bonding information)
6542 * with the specified atom. Returns the reference to the new atom.
6543 * This operation is undoable.
6546 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6552 VALUE retval, aval, ival;
6554 Data_Get_Struct(self, Molecule, mol);
6555 rb_scan_args(argc, argv, "11", &aval, &ival);
6556 if (FIXNUM_P(aval)) {
6557 int idx = NUM2INT(aval);
6558 if (idx < 0 || idx >= mol->natoms)
6559 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6560 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6562 apsrc = s_AtomFromValue(aval);
6565 rb_raise(rb_eMolbyError, "bad atom specification");
6567 pos = NUM2INT(rb_Integer(ival));
6569 AtomDuplicate(&arec, apsrc);
6570 arec.connect.count = 0;
6571 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6574 aref = AtomRefNew(mol, pos);
6575 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6583 * create_bond(n1, n2, ...) -> Integer
6585 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6586 * do nothing for that pair. Returns the number of bonds actually created.
6587 * This operation is undoable.
6590 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6593 Int i, j, k, *ip, old_nbonds;
6595 rb_raise(rb_eMolbyError, "missing arguments");
6597 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6598 Data_Get_Struct(self, Molecule, mol);
6599 ip = ALLOC_N(Int, argc + 1);
6600 for (i = j = 0; i < argc; i++, j++) {
6601 ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6603 if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
6604 j -= 2; /* This bond is already present: skip it */
6606 for (k = 0; k < j - 1; k += 2) {
6607 if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
6608 j -= 2; /* The same entry is already in the argument */
6615 old_nbonds = mol->nbonds;
6617 ip[j] = kInvalidIndex;
6618 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
6622 rb_raise(rb_eMolbyError, "atom index out of range");
6624 rb_raise(rb_eMolbyError, "too many bonds");
6626 rb_raise(rb_eMolbyError, "duplicate bonds");
6628 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6630 rb_raise(rb_eMolbyError, "error in creating bonds");
6631 return INT2NUM(mol->nbonds - old_nbonds);
6636 * molecule.remove_bonds(n1, n2, ...) -> Integer
6638 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6639 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6640 * This operation is undoable.
6643 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6649 rb_raise(rb_eMolbyError, "missing arguments");
6651 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6652 Data_Get_Struct(self, Molecule, mol);
6654 for (i = j = 0; i < argc; i++, j = 1 - j) {
6655 n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6657 Int k = MoleculeLookupBond(mol, n[0], n[1]);
6661 IntGroupAdd(bg, k, 1);
6666 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
6667 i = IntGroupGetCount(bg);
6668 IntGroupRelease(bg);
6675 * assign_bond_order(idx, d1)
6676 * assign_bond_orders(group, [d1, d2, ...])
6678 * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
6679 * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
6680 * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
6681 * (This may change in the future)
6682 * This operation is undoable.
6685 s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
6689 Data_Get_Struct(self, Molecule, mol);
6690 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6691 /* The first form */
6692 Int idx = NUM2INT(rb_Integer(idxval));
6693 Double d1 = NUM2DBL(rb_Float(dval));
6694 if (idx < 0 || idx >= mol->nbonds)
6695 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6696 ig = IntGroupNewWithPoints(idx, 1, -1);
6697 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
6698 IntGroupRelease(ig);
6702 ig = IntGroupFromValue(idxval);
6703 n = IntGroupGetCount(ig);
6705 rb_raise(rb_eMolbyError, "the bond index is empty");
6706 dval = rb_ary_to_ary(dval);
6707 dp = (Double *)calloc(sizeof(Double), n);
6708 for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
6709 dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
6711 MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
6713 IntGroupRelease(ig);
6720 * get_bond_order(idx) -> Float
6721 * get_bond_orders(group) -> Array
6723 * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
6724 * In the second form, the bond orders at the indices in the group are returned as an array.
6725 * If no bond order information have been assigned, returns nil (the first form)
6726 * or an empty array (the second form).
6729 s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
6735 Int i, n, numericArg;
6736 Data_Get_Struct(self, Molecule, mol);
6737 if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
6738 /* The first form */
6739 Int idx = NUM2INT(rb_Integer(idxval));
6740 if (idx < 0 || idx >= mol->nbonds)
6741 rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
6742 if (mol->bondOrders == NULL)
6744 ig = IntGroupNewWithPoints(idx, 1, -1);
6748 if (mol->bondOrders == NULL)
6749 return rb_ary_new();
6750 ig = IntGroupFromValue(idxval);
6751 n = IntGroupGetCount(ig);
6753 rb_raise(rb_eMolbyError, "the bond index is empty");
6756 dp = (Double *)calloc(sizeof(Double), n);
6757 MoleculeGetBondOrders(mol, dp, ig);
6759 retval = rb_float_new(dp[0]);
6761 retval = rb_ary_new();
6762 for (i = 0; i < n; i++)
6763 rb_ary_push(retval, rb_float_new(dp[i]));
6766 IntGroupRelease(ig);
6772 * bond_exist?(idx1, idx2) -> bool
6774 * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
6775 * Imaginary bonds between a pi-anchor and member atoms are not considered.
6778 s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
6784 Data_Get_Struct(self, Molecule, mol);
6785 idx1 = NUM2INT(rb_Integer(ival1));
6786 idx2 = NUM2INT(rb_Integer(ival2));
6787 if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
6788 rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
6789 ap = ATOM_AT_INDEX(mol->atoms, idx1);
6790 cp = AtomConnectData(&ap->connect);
6791 for (i = 0; i < ap->connect.count; i++) {
6800 * add_angle(n1, n2, n3) -> Molecule
6802 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6803 * when a bond is created, so it is rarely necessary to use this method explicitly.
6804 * This operation is undoable.
6807 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6811 Data_Get_Struct(self, Molecule, mol);
6812 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6813 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6814 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6815 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6816 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6817 n[3] = kInvalidIndex;
6818 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6824 * remove_angle(n1, n2, n3) -> Molecule
6826 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6827 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6828 * This operation is undoable.
6831 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6836 Data_Get_Struct(self, Molecule, mol);
6837 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6838 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6839 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6840 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6841 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6842 ig = IntGroupNewWithPoints(n[3], 1, -1);
6843 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6844 IntGroupRelease(ig);
6850 * add_dihedral(n1, n2, n3, n4) -> Molecule
6852 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6853 * when a bond is created, so it is rarely necessary to use this method explicitly.
6854 * This operation is undoable.
6857 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6861 Data_Get_Struct(self, Molecule, mol);
6862 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6863 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6864 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6865 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6866 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6867 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6868 n[4] = kInvalidIndex;
6869 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6875 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6877 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6878 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6879 * This operation is undoable.
6882 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6887 Data_Get_Struct(self, Molecule, mol);
6888 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6889 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6890 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6891 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6892 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6893 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6894 ig = IntGroupNewWithPoints(n[4], 1, -1);
6895 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6896 IntGroupRelease(ig);
6902 * add_improper(n1, n2, n3, n4) -> Molecule
6904 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6905 * not automatically added when a new bond is created, so this method is more useful than
6906 * the angle/dihedral counterpart.
6907 * This operation is undoable.
6910 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6914 Data_Get_Struct(self, Molecule, mol);
6915 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6916 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6917 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6918 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6919 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6920 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6921 n[4] = kInvalidIndex;
6922 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6928 * remove_improper(n1, n2, n3, n4) -> Molecule
6929 * remove_improper(intgroup) -> Molecule
6931 * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
6932 * Returns self. Unlike angles and dihedrals, impropers are
6933 * not automatically added when a new bond is created, so this method is more useful than
6934 * the angle/dihedral counterpart.
6935 * This operation is undoable.
6938 s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
6941 VALUE v1, v2, v3, v4;
6944 Data_Get_Struct(self, Molecule, mol);
6946 ig = IntGroupFromValue(argv[0]);
6948 rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
6949 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6950 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6951 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6952 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6953 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6954 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6955 ig = IntGroupNewWithPoints(n[4], 1, -1);
6957 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6958 IntGroupRelease(ig);
6964 * assign_residue(group, res) -> Molecule
6966 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6967 * or "resname.resno". When the residue number is not specified, the residue number of
6968 * the first atom in the group is used.
6969 * This operation is undoable.
6972 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6976 char *p, *pp, buf[16];
6979 Data_Get_Struct(self, Molecule, mol);
6981 /* Parse the argument res */
6982 if (FIXNUM_P(res)) {
6983 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6984 resid = NUM2INT(res);
6987 p = StringValuePtr(res);
6988 pp = strchr(p, '.');
6990 resid = atoi(pp + 1);
6996 if (n > sizeof buf - 1)
7001 ig = s_Molecule_AtomGroupFromValue(self, range);
7002 if (ig == NULL || IntGroupGetCount(ig) == 0)
7006 /* Use the residue number of the first specified atom */
7007 n = IntGroupGetNthPoint(ig, 0);
7008 if (n >= mol->natoms)
7009 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
7010 ap = ATOM_AT_INDEX(mol->atoms, n);
7013 /* Change the residue number */
7014 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
7015 /* Change the residue name if necessary */
7019 seqs[1] = kInvalidIndex; */
7020 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
7022 IntGroupRelease(ig);
7028 * offset_residue(group, offset) -> Molecule
7030 * Offset the residue number of the specified atoms. If any of the residue number gets
7031 * negative, then exception is thrown.
7032 * This operation is undoable.
7035 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
7040 Data_Get_Struct(self, Molecule, mol);
7041 ig = s_Molecule_AtomGroupFromValue(self, range);
7042 ofs = NUM2INT(offset);
7043 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
7045 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
7046 IntGroupRelease(ig);
7052 * renumber_atoms(array) -> IntGroup
7054 * Change the order of atoms so that the atoms specified in the array argument appear
7055 * in this order from the top of the molecule. The atoms that are not included in array
7056 * are placed after these atoms, and these atoms are returned as an intGroup.
7057 * This operation is undoable.
7060 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
7066 VALUE *valp, retval;
7067 Data_Get_Struct(self, Molecule, mol);
7068 if (TYPE(array) != T_ARRAY)
7069 array = rb_funcall(array, rb_intern("to_a"), 0);
7070 n = RARRAY_LEN(array);
7071 valp = RARRAY_PTR(array);
7072 new2old = ALLOC_N(Int, n + 1);
7073 for (i = 0; i < n; i++)
7074 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
7075 new2old[i] = kInvalidIndex;
7076 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
7078 rb_raise(rb_eMolbyError, "Atom index out of range");
7080 rb_raise(rb_eMolbyError, "Duplicate entry");
7082 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
7083 retval = IntGroup_Alloc(rb_cIntGroup);
7084 Data_Get_Struct(retval, IntGroup, ig);
7085 if (mol->natoms > n)
7086 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
7093 * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
7095 * Find atoms that are within the threshold distance from the given atom.
7096 * (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.)
7097 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7098 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7099 * If limit is not given, a default value of 1.2 is used.
7100 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
7103 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
7106 VALUE aval, limval, radval;
7107 double limit, radius;
7108 Int n1, nbonds, *bonds, an;
7110 Data_Get_Struct(self, Molecule, mol);
7111 rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
7112 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)) {
7113 VectorFromValue(aval, &v);
7115 radius = gElementParameters[6].radius;
7117 radius = NUM2DBL(rb_Float(radval));
7120 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
7121 v = ATOM_AT_INDEX(mol->atoms, n1)->r;
7122 an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
7123 if (an >= 0 && an < gCountElementParameters)
7124 radius = gElementParameters[an].radius;
7125 else radius = gElementParameters[6].radius;
7130 limit = NUM2DBL(rb_Float(limval));
7131 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
7133 MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
7134 aval = rb_ary_new();
7136 for (n1 = 0; n1 < nbonds; n1++)
7137 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
7145 * guess_bonds(limit = 1.2) -> Integer
7147 * Create bonds between atoms that are within the threshold distance.
7148 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
7149 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
7150 * If limit is not given, a default value of 1.2 is used.
7151 * The number of the newly created bonds is returned.
7152 * This operation is undoable.
7155 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
7161 Data_Get_Struct(self, Molecule, mol);
7162 rb_scan_args(argc, argv, "01", &limval);
7166 limit = NUM2DBL(rb_Float(limval));
7167 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
7169 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
7172 return INT2NUM(nbonds);
7177 * register_undo(script, *args)
7179 * Register an undo operation with the current molecule.
7182 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
7187 Data_Get_Struct(self, Molecule, mol);
7188 rb_scan_args(argc, argv, "1*", &script, &args);
7189 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
7190 MolActionCallback_registerUndo(mol, act);
7196 * undo_enabled? -> bool
7198 * Returns true if undo is enabled for this molecule; otherwise no.
7201 s_Molecule_UndoEnabled(VALUE self)
7204 Data_Get_Struct(self, Molecule, mol);
7205 if (MolActionCallback_isUndoRegistrationEnabled(mol))
7212 * undo_enabled = bool
7214 * Enable or disable undo.
7217 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
7220 Data_Get_Struct(self, Molecule, mol);
7221 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
7227 * selection -> IntGroup
7229 * Returns the current selection.
7232 s_Molecule_Selection(VALUE self)
7237 Data_Get_Struct(self, Molecule, mol);
7238 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
7239 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
7240 val = ValueFromIntGroup(ig);
7241 IntGroupRelease(ig);
7243 val = IntGroup_Alloc(rb_cIntGroup);
7249 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
7253 Data_Get_Struct(self, Molecule, mol);
7257 ig = s_Molecule_AtomGroupFromValue(self, val);
7259 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
7261 MoleculeSetSelection(mol, ig);
7263 IntGroupRelease(ig);
7269 * selection = IntGroup
7271 * Set the current selection. The right-hand operand may be nil.
7272 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
7275 s_Molecule_SetSelection(VALUE self, VALUE val)
7277 return s_Molecule_SetSelectionSub(self, val, 0);
7282 * set_undoable_selection(IntGroup)
7284 * Set the current selection with undo registration. The right-hand operand may be nil.
7285 * This operation is undoable.
7288 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
7290 return s_Molecule_SetSelectionSub(self, val, 1);
7295 * hidden_atoms -> IntGroup
7297 * Returns the currently hidden atoms.
7300 s_Molecule_HiddenAtoms(VALUE self)
7302 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7303 return Qnil; /* Not reached */
7307 Data_Get_Struct(self, Molecule, mol);
7312 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7313 if (ap->exflags & kAtomHiddenFlag)
7314 IntGroupAdd(ig, i, 1);
7316 val = ValueFromIntGroup(ig);
7317 IntGroupRelease(ig);
7320 } else return Qnil; */
7325 * set_hidden_atoms(IntGroup)
7326 * self.hidden_atoms = IntGroup
7328 * Hide the specified atoms. This operation is _not_ undoable.
7331 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
7333 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
7334 return Qnil; /* Not reached */
7337 Data_Get_Struct(self, Molecule, mol);
7345 ig = s_Molecule_AtomGroupFromValue(self, val);
7346 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7347 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
7348 ap->exflags |= kAtomHiddenFlag;
7350 ap->exflags &= kAtomHiddenFlag;
7354 IntGroupRelease(ig);
7355 MoleculeCallback_notifyModification(mol, 0);
7362 * select_frame(index)
7365 * Select the specified frame. If successful, returns true, otherwise returns false.
7368 s_Molecule_SelectFrame(VALUE self, VALUE val)
7371 int ival = NUM2INT(val);
7372 Data_Get_Struct(self, Molecule, mol);
7373 ival = MoleculeSelectFrame(mol, ival, 1);
7383 * Get the current frame.
7386 s_Molecule_Frame(VALUE self)
7389 Data_Get_Struct(self, Molecule, mol);
7390 return INT2NUM(mol->cframe);
7395 * nframes -> Integer
7397 * Get the number of frames.
7400 s_Molecule_Nframes(VALUE self)
7403 Data_Get_Struct(self, Molecule, mol);
7404 return INT2NUM(MoleculeGetNumberOfFrames(mol));
7409 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
7410 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
7412 * Insert new frames at the indices specified by the intGroup. If the first argument is
7413 * an integer, a single new frame is inserted at that index. If the first argument is
7414 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
7415 * should be an array of arrays of Vector3Ds, then those coordinates are set
7416 * to the new frame. Otherwise, the coordinates of current molecule are copied
7418 * Returns an intGroup representing the inserted frames if successful, nil if not.
7421 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
7423 VALUE val, coords, cells;
7426 int count, ival, i, j, len, len_c, len2, nframes;
7429 Data_Get_Struct(self, Molecule, mol);
7430 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
7431 if (coords != Qnil) {
7432 if (TYPE(coords) != T_ARRAY)
7433 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
7434 len = RARRAY_LEN(coords);
7436 if (cells != Qnil) {
7437 if (mol->cell == NULL)
7438 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
7439 if (TYPE(cells) != T_ARRAY)
7440 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
7441 len_c = RARRAY_LEN(cells);
7443 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
7444 nframes = MoleculeGetNumberOfFrames(mol);
7446 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
7447 val = ValueFromIntGroup(ig);
7449 ig = IntGroupFromValue(val);
7451 count = IntGroupGetCount(ig); /* Count is updated here */
7452 vp = ALLOC_N(Vector, mol->natoms * count);
7454 vp2 = ALLOC_N(Vector, 4 * count);
7458 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
7459 ptr = RARRAY_PTR(coords);
7460 for (i = 0; i < count; i++) {
7461 if (TYPE(ptr[i]) != T_ARRAY)
7462 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
7463 len2 = RARRAY_LEN(ptr[i]);
7464 if (len2 < mol->natoms)
7465 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
7466 ptr2 = RARRAY_PTR(ptr[i]);
7467 for (j = 0; j < mol->natoms; j++)
7468 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
7472 for (i = 0; i < count; i++) {
7473 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7474 vp[i * mol->natoms + j] = ap->r;
7480 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
7481 ptr = RARRAY_PTR(cells);
7482 for (i = 0; i < count; i++) {
7483 if (TYPE(ptr[i]) != T_ARRAY)
7484 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
7485 len2 = RARRAY_LEN(ptr[i]);
7487 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
7488 ptr2 = RARRAY_PTR(ptr[i]);
7489 for (j = 0; j < 4; j++)
7490 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
7493 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
7494 IntGroupRelease(ig);
7498 return (ival >= 0 ? val : Qnil);
7503 * create_frame(coordinates = nil) -> Integer
7504 * create_frames(coordinates = nil) -> Integer
7506 * Same as molecule.insert_frames(nil, coordinates).
7509 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
7512 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
7514 return s_Molecule_InsertFrames(3, vals, self);
7519 * remove_frames(IntGroup, wantCoordinates = false)
7521 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
7522 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
7523 * removed frames is returned if operation is successful.
7526 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
7533 Data_Get_Struct(self, Molecule, mol);
7534 rb_scan_args(argc, argv, "11", &val, &flag);
7535 ig = IntGroupFromValue(val);
7536 count = IntGroupGetCount(ig);
7538 /* Create return value before removing frames */
7543 retval = rb_ary_new2(count);
7544 for (i = 0; i < count; i++) {
7545 n = IntGroupGetNthPoint(ig, i);
7546 coords = rb_ary_new2(mol->natoms);
7547 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
7548 if (n < ap->nframes && n != mol->cframe)
7551 rb_ary_push(coords, ValueFromVector(&v));
7553 rb_ary_push(retval, coords);
7555 } else retval = Qtrue;
7556 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
7563 * each_frame {|n| ...}
7565 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
7566 * the frame number. After completion, the original frame number is restored.
7569 s_Molecule_EachFrame(VALUE self)
7571 int i, cframe, nframes;
7573 Data_Get_Struct(self, Molecule, mol);
7574 cframe = mol->cframe;
7575 nframes = MoleculeGetNumberOfFrames(mol);
7577 for (i = 0; i < nframes; i++) {
7578 MoleculeSelectFrame(mol, i, 1);
7579 rb_yield(INT2NUM(i));
7581 MoleculeSelectFrame(mol, cframe, 1);
7588 * set_atom_attr(index, key, value)
7590 * Set the atom attribute for the specified atom.
7591 * This operation is undoable.
7594 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7598 Data_Get_Struct(self, Molecule, mol);
7599 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7600 oldval = s_AtomRef_GetAttr(aref, key);
7603 s_AtomRef_SetAttr(aref, key, val);
7609 * get_atom_attr(index, key)
7611 * Get the atom attribute for the specified atom.
7614 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7616 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7621 * get_coord_from_frame(index, group = nil)
7623 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7624 * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
7625 * copied; now they are always copied)
7628 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7631 VALUE ival, gval, cval;
7632 Int index, i, j, n, nn;
7634 IntGroupIterator iter;
7637 Data_Get_Struct(self, Molecule, mol);
7638 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7640 rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
7641 index = NUM2INT(rb_Integer(ival));
7642 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7644 rb_raise(rb_eMolbyError, "No frame is present");
7646 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7649 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7651 ig = s_Molecule_AtomGroupFromValue(self, gval);
7653 n = IntGroupGetCount(ig);
7655 vp = (Vector *)calloc(sizeof(Vector), n);
7656 IntGroupIteratorInit(ig, &iter);
7659 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7660 ap = ATOM_AT_INDEX(mol->atoms, i);
7661 if (index < ap->nframes) {
7662 vp[j] = ap->frames[index];
7670 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7672 if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7673 vp = mol->frame_cells + index * 4;
7674 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7676 IntGroupIteratorRelease(&iter);
7678 IntGroupRelease(ig);
7684 * fragment(n1, *exatoms) -> IntGroup
7685 * fragment(group, *exatoms) -> IntGroup
7687 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7688 * those atoms will not be counted during the search.
7691 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7694 IntGroup *baseg, *ig, *exatoms;
7696 volatile VALUE nval, exval;
7697 Data_Get_Struct(self, Molecule, mol);
7698 rb_scan_args(argc, argv, "1*", &nval, &exval);
7699 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7701 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7703 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7705 if (RARRAY_LEN(exval) == 0) {
7708 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7709 Data_Get_Struct(exval, IntGroup, exatoms);
7711 if (baseg == NULL) {
7712 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7714 IntGroupIterator iter;
7715 IntGroupIteratorInit(baseg, &iter);
7716 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7719 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7721 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7723 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7725 IntGroupAddIntGroup(ig, subg);
7726 IntGroupRelease(subg);
7731 IntGroupIteratorRelease(&iter);
7732 IntGroupRelease(baseg);
7735 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7736 nval = ValueFromIntGroup(ig);
7737 IntGroupRelease(ig);
7743 * fragments(exclude = nil)
7745 * Returns the fragments as an array of IntGroups.
7746 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7747 * in defining the fragment.
7750 s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
7753 IntGroup *ag, *fg, *eg;
7754 VALUE gval, exval, retval;
7755 Data_Get_Struct(self, Molecule, mol);
7758 if (mol->natoms == 0)
7759 return rb_ary_new();
7760 rb_scan_args(argc, argv, "01", &exval);
7764 eg = IntGroupFromValue(exval);
7765 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7767 IntGroupRemoveIntGroup(ag, eg);
7768 retval = rb_ary_new();
7769 while (IntGroupGetCount(ag) > 0) {
7770 int n = IntGroupGetNthPoint(ag, 0);
7771 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7773 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7774 gval = ValueFromIntGroup(fg);
7775 rb_ary_push(retval, gval);
7776 IntGroupRemoveIntGroup(ag, fg);
7777 IntGroupRelease(fg);
7779 IntGroupRelease(ag);
7781 IntGroupRelease(eg);
7787 * each_fragment(exclude = nil) {|group| ...}
7789 * Execute the block, with the IntGroup object for each fragment as the argument.
7790 * Atoms or bonds should not be added or removed during the execution of the block.
7791 * If exclude is given (as an array or an IntGroup), then those atoms are excluded
7792 * in defining the fragment.
7795 s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
7798 IntGroup *ag, *fg, *eg;
7800 Data_Get_Struct(self, Molecule, mol);
7801 if (mol == NULL || mol->natoms == 0)
7803 rb_scan_args(argc, argv, "01", &exval);
7807 eg = IntGroupFromValue(exval);
7808 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7810 IntGroupRemoveIntGroup(ag, eg);
7811 while (IntGroupGetCount(ag) > 0) {
7812 int n = IntGroupGetNthPoint(ag, 0);
7813 fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
7815 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7816 gval = ValueFromIntGroup(fg);
7818 IntGroupRemoveIntGroup(ag, fg);
7819 IntGroupRelease(fg);
7821 IntGroupRelease(ag);
7823 IntGroupRelease(eg);
7829 * detachable?(group) -> [n1, n2]
7831 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7832 * of the molecule via only one bond. If it is, then the indices of the atoms
7833 * belonging to the bond is returned, the first element being the atom included
7834 * in the fragment. Otherwise, Qnil is returned.
7837 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7843 Data_Get_Struct(self, Molecule, mol);
7844 ig = s_Molecule_AtomGroupFromValue(self, gval);
7845 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7846 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7847 } else retval = Qnil;
7848 IntGroupRelease(ig);
7854 * bonds_on_border(group = selection) -> Array of Array of two Integers
7856 * Returns an array of bonds that connect an atom in the group and an atom out
7857 * of the group. The first atom in the bond always belongs to the group. If no
7858 * such bonds are present, an empty array is returned.
7861 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7866 Data_Get_Struct(self, Molecule, mol);
7867 rb_scan_args(argc, argv, "01", &gval);
7869 ig = MoleculeGetSelection(mol);
7873 ig = s_Molecule_AtomGroupFromValue(self, gval);
7875 retval = rb_ary_new();
7878 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7880 IntGroupIterator iter;
7882 IntGroupIteratorInit(bg, &iter);
7883 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7884 /* The atoms at the border */
7886 n1 = mol->bonds[i * 2];
7887 n2 = mol->bonds[i * 2 + 1];
7888 if (IntGroupLookupPoint(ig, n1) < 0) {
7892 if (IntGroupLookupPoint(ig, n1) < 0)
7893 continue; /* Actually this is an internal error */
7895 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7897 IntGroupIteratorRelease(&iter);
7899 IntGroupRelease(bg);
7900 IntGroupRelease(ig);
7906 * translate(vec, group = nil) -> Molecule
7908 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7909 * This operation is undoable.
7912 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7918 Data_Get_Struct(self, Molecule, mol);
7919 rb_scan_args(argc, argv, "11", &vec, &group);
7920 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7921 VectorFromValue(vec, &v);
7922 // MoleculeTranslate(mol, &v, ig);
7923 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7925 IntGroupRelease(ig);
7931 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7933 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7934 * If group is given, only atoms in the group are moved.
7935 * This operation is undoable.
7938 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7941 volatile VALUE aval, anval, cval, gval;
7946 Data_Get_Struct(self, Molecule, mol);
7947 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7948 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7949 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7950 VectorFromValue(aval, &av);
7952 cv.x = cv.y = cv.z = 0.0;
7954 VectorFromValue(cval, &cv);
7955 if (TransformForRotation(tr, &av, angle, &cv))
7956 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7957 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7959 IntGroupRelease(ig);
7965 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7967 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7968 * axis must not be a zero vector.
7969 * If group is given, only atoms in the group are moved.
7970 * This operation is undoable.
7973 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7976 volatile VALUE aval, cval, gval;
7980 Data_Get_Struct(self, Molecule, mol);
7981 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7982 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7983 VectorFromValue(aval, &av);
7985 cv.x = cv.y = cv.z = 0.0;
7987 VectorFromValue(cval, &cv);
7988 if (TransformForReflection(tr, &av, &cv))
7989 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7990 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7992 IntGroupRelease(ig);
7998 * invert(center = [0,0,0], group = nil) -> Molecule
8000 * Invert the molecule with the given center.
8001 * If group is given, only atoms in the group are moved.
8002 * This operation is undoable.
8005 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
8008 volatile VALUE cval, gval;
8012 Data_Get_Struct(self, Molecule, mol);
8013 rb_scan_args(argc, argv, "02", &cval, &gval);
8014 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
8016 cv.x = cv.y = cv.z = 0.0;
8018 VectorFromValue(cval, &cv);
8019 TransformForInversion(tr, &cv);
8020 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8022 IntGroupRelease(ig);
8028 * transform(transform, group = nil) -> Molecule
8030 * Transform the molecule by the given Transform object.
8031 * If group is given, only atoms in the group are moved.
8032 * This operation is undoable.
8035 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
8041 Data_Get_Struct(self, Molecule, mol);
8042 rb_scan_args(argc, argv, "11", &trans, &group);
8043 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8044 TransformFromValue(trans, &tr);
8045 /* MoleculeTransform(mol, tr, ig); */
8046 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
8048 IntGroupRelease(ig);
8053 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
8055 switch (MoleculeCenterOfMass(mol, outv, ig)) {
8056 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
8057 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
8059 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
8065 * center_of_mass(group = nil) -> Vector3D
8067 * Calculate the center of mass for the given set of atoms. The argument
8068 * group is null, then all atoms are considered.
8071 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
8077 Data_Get_Struct(self, Molecule, mol);
8078 rb_scan_args(argc, argv, "01", &group);
8079 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8080 s_Molecule_DoCenterOfMass(mol, &v, ig);
8082 IntGroupRelease(ig);
8083 return ValueFromVector(&v);
8088 * centralize(group = nil) -> self
8090 * Translate the molecule so that the center of mass of the given group is located
8091 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
8094 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
8100 Data_Get_Struct(self, Molecule, mol);
8101 rb_scan_args(argc, argv, "01", &group);
8102 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8103 s_Molecule_DoCenterOfMass(mol, &v, ig);
8105 IntGroupRelease(ig);
8109 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
8115 * bounds(group = nil) -> [min, max]
8117 * Calculate the boundary. The return value is an array of two Vector3D objects.
8120 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
8128 Data_Get_Struct(self, Molecule, mol);
8129 rb_scan_args(argc, argv, "01", &group);
8130 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
8131 if (ig != NULL && IntGroupGetCount(ig) == 0)
8132 rb_raise(rb_eMolbyError, "atom group is empty");
8133 vmin.x = vmin.y = vmin.z = 1e30;
8134 vmax.x = vmax.y = vmax.z = -1e30;
8135 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
8137 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
8153 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
8156 /* Get atom position or a vector */
8158 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
8160 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
8161 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
8162 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
8164 VectorFromValue(val, vp);
8170 * measure_bond(n1, n2) -> Float
8172 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
8173 * or Vector3D values.
8174 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8177 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
8181 Data_Get_Struct(self, Molecule, mol);
8182 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8183 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8184 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
8189 * measure_angle(n1, n2, n3) -> Float
8191 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
8192 * or Vector3D values. The return value is in degree.
8193 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8196 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
8201 Data_Get_Struct(self, Molecule, mol);
8202 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8203 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8204 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8205 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
8207 return Qnil; /* Cannot define */
8208 else return rb_float_new(d);
8213 * measure_dihedral(n1, n2, n3, n4) -> Float
8215 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
8216 * or Vector3D values. The return value is in degree.
8217 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
8220 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
8223 Vector v1, v2, v3, v4;
8225 Data_Get_Struct(self, Molecule, mol);
8226 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
8227 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
8228 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
8229 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
8230 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
8232 return Qnil; /* Cannot define */
8233 else return rb_float_new(d);
8238 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
8240 * Expand the specified part of the molecule by the given symmetry operation.
8241 * Returns the array of atom indices corresponding to the expanded atoms.
8244 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
8247 VALUE gval, sval, xval, yval, zval, rval;
8253 Data_Get_Struct(self, Molecule, mol);
8254 rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
8255 n[0] = NUM2INT(rb_Integer(sval));
8256 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
8257 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
8258 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
8259 ig = s_Molecule_AtomGroupFromValue(self, gval);
8260 if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
8261 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
8262 natoms = mol->natoms;
8264 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
8266 rval = rb_ary_new2(nidx);
8267 while (--nidx >= 0) {
8268 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
8270 /* if (natoms == mol->natoms)
8273 rval = IntGroup_Alloc(rb_cIntGroup);
8274 Data_Get_Struct(rval, IntGroup, ig);
8275 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
8282 * amend_by_symmetry(group = nil) -> IntGroup
8284 * Expand the specified part of the molecule by the given symmetry operation.
8285 * Returns an IntGroup containing the added atoms.
8288 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
8293 Data_Get_Struct(self, Molecule, mol);
8294 rb_scan_args(argc, argv, "01", &gval);
8296 ig = s_Molecule_AtomGroupFromValue(self, gval);
8298 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
8299 rval = ValueFromIntGroup(ig2);
8300 IntGroupRelease(ig2);
8306 * transform_for_symop(symop, is_cartesian = nil) -> Transform
8308 * Get the transform corresponding to the symmetry operation. The symop can either be
8309 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
8310 * If is_cartesian is true, the returned transform is for cartesian coordinates.
8311 * Otherwise, the returned transform is for fractional coordinates.
8312 * Raises exception when no cell or no transform are defined.
8315 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
8321 Data_Get_Struct(self, Molecule, mol);
8322 if (mol->cell == NULL)
8323 rb_raise(rb_eMolbyError, "no unit cell is defined");
8324 if (mol->nsyms == 0)
8325 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8326 rb_scan_args(argc, argv, "11", &sval, &fval);
8327 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
8328 symop.sym = NUM2INT(rb_Integer(sval));
8329 symop.dx = symop.dy = symop.dz = 0;
8331 sval = rb_ary_to_ary(sval);
8332 if (RARRAY_LEN(sval) < 4)
8333 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
8334 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
8335 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
8336 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
8337 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
8339 if (symop.sym >= mol->nsyms)
8340 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
8341 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
8342 return ValueFromTransform(&tr);
8347 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
8349 * Get the symmetry operation corresponding to the given transform.
8350 * If is_cartesian is true, the given transform is for cartesian coordinates.
8351 * Otherwise, the given transform is for fractional coordinates.
8352 * Raises exception when no cell or no transform are defined.
8355 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
8362 Data_Get_Struct(self, Molecule, mol);
8363 if (mol->cell == NULL)
8364 rb_raise(rb_eMolbyError, "no unit cell is defined");
8365 if (mol->nsyms == 0)
8366 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
8367 rb_scan_args(argc, argv, "11", &tval, &fval);
8368 TransformFromValue(tval, &tr);
8369 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
8371 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
8373 return Qnil; /* Not found */
8379 * wrap_unit_cell(group) -> Vector3D
8381 * Move the specified group so that the center of mass of the group is within the
8382 * unit cell. The offset vector is returned. If no periodic box is defined,
8383 * exception is raised.
8386 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
8391 Data_Get_Struct(self, Molecule, mol);
8392 if (mol->cell == NULL)
8393 rb_raise(rb_eMolbyError, "no unit cell is defined");
8394 ig = s_Molecule_AtomGroupFromValue(self, gval);
8395 s_Molecule_DoCenterOfMass(mol, &cv, ig);
8396 TransformVec(&v, mol->cell->rtr, &cv);
8397 if (mol->cell->flags[0])
8399 if (mol->cell->flags[1])
8401 if (mol->cell->flags[2])
8403 TransformVec(&dv, mol->cell->tr, &v);
8405 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
8406 IntGroupRelease(ig);
8407 return ValueFromVector(&dv);
8412 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
8414 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
8415 * first and second atom in the pair should belong to group1 and group2, respectively.
8416 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
8419 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
8422 VALUE limval, gval1, gval2, rval, igval;
8423 IntGroup *ig1, *ig2;
8424 IntGroupIterator iter1, iter2;
8430 MDExclusion *exinfo;
8433 Data_Get_Struct(self, Molecule, mol);
8434 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
8435 lim = NUM2DBL(rb_Float(limval));
8437 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
8439 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
8441 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
8443 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
8445 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
8447 if (!RTEST(igval)) {
8448 /* Use the exclusion table in MDArena */
8449 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
8450 VALUE mval = ValueFromMolecule(mol);
8451 s_RebuildMDParameterIfNecessary(mval, Qnil);
8453 exinfo = mol->arena->exinfo; /* May be NULL */
8454 exlist = mol->arena->exlist;
8459 IntGroupIteratorInit(ig1, &iter1);
8460 IntGroupIteratorInit(ig2, &iter2);
8463 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
8465 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
8467 if (exinfo != NULL) {
8468 exn1 = exinfo[n[0]].index1;
8469 exn2 = exinfo[n[0] + 1].index1;
8470 } else exn1 = exn2 = -1;
8471 IntGroupIteratorReset(&iter2);
8472 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
8473 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
8475 continue; /* Same atom */
8476 if (exinfo != NULL) {
8477 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
8478 for (i = exn1; i < exn2; i++) {
8479 if (exlist[i] == n[1])
8483 continue; /* Should be excluded */
8485 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
8486 /* Is this pair already registered? */
8488 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
8489 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
8493 /* Not registered yet */
8494 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
8499 IntGroupIteratorRelease(&iter2);
8500 IntGroupIteratorRelease(&iter1);
8501 IntGroupRelease(ig2);
8502 IntGroupRelease(ig1);
8503 rval = rb_ary_new2(npairs);
8504 if (pairs != NULL) {
8505 for (i = 0; i < npairs; i++) {
8506 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
8513 /* Calculate the transform that moves the current coordinates to the reference
8514 coordinates with least displacements. */
8516 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
8524 Double eigen_val[3];
8525 Vector eigen_vec[3];
8527 IntGroupIterator iter;
8529 natoms = mol->natoms;
8531 IntGroupIteratorInit(ig, &iter);
8533 /* Calculate the weighted center */
8537 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8538 ap1 = ATOM_AT_INDEX(ap, in);
8539 w1 = (weights != NULL ? weights[i] : ap1->weight);
8540 VecScaleInc(org1, ap1->r, w1);
8541 VecScaleInc(org2, ref[i], w1);
8545 VecScaleSelf(org1, w);
8546 VecScaleSelf(org2, w);
8548 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
8549 /* Matrix to diagonalize = R * tR */
8550 memset(r, 0, sizeof(Mat33));
8551 memset(q, 0, sizeof(Mat33));
8552 memset(u, 0, sizeof(Mat33));
8554 IntGroupIteratorReset(&iter);
8555 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8557 ap1 = ATOM_AT_INDEX(ap, in);
8558 w1 = (weights != NULL ? weights[i] : ap1->weight);
8560 VecSub(v1, ap1->r, org1);
8561 VecSub(v2, ref[i], org2);
8562 r[0] += w1 * v1.x * v2.x;
8563 r[1] += w1 * v1.y * v2.x;
8564 r[2] += w1 * v1.z * v2.x;
8565 r[3] += w1 * v1.x * v2.y;
8566 r[4] += w1 * v1.y * v2.y;
8567 r[5] += w1 * v1.z * v2.y;
8568 r[6] += w1 * v1.x * v2.z;
8569 r[7] += w1 * v1.y * v2.z;
8570 r[8] += w1 * v1.z * v2.z;
8573 for (i = 0; i < 9; i++)
8575 for (i = 0; i < 3; i++) {
8576 for (j = 0; j < 3; j++) {
8577 for (k = 0; k < 3; k++) {
8578 q[i+j*3] += r[i+k*3] * r[j+k*3];
8583 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
8584 IntGroupIteratorRelease(&iter);
8585 return -1.0; /* Cannot determine the eigenvector */
8588 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
8589 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
8590 MatrixTranspose(r, r);
8591 for (i = 0; i < 3; i++) {
8592 MatrixVec(&s[i], r, &eigen_vec[i]);
8593 w1 = 1.0 / sqrt(eigen_val[i]);
8594 VecScaleSelf(s[i], w1);
8596 for (k = 0; k < 3; k++) {
8597 u[0] += s[k].x * eigen_vec[k].x;
8598 u[1] += s[k].y * eigen_vec[k].x;
8599 u[2] += s[k].z * eigen_vec[k].x;
8600 u[3] += s[k].x * eigen_vec[k].y;
8601 u[4] += s[k].y * eigen_vec[k].y;
8602 u[5] += s[k].z * eigen_vec[k].y;
8603 u[6] += s[k].x * eigen_vec[k].z;
8604 u[7] += s[k].y * eigen_vec[k].z;
8605 u[8] += s[k].z * eigen_vec[k].z;
8608 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
8609 MatrixVec(&org1, u, &org1);
8611 for (i = 0; i < 9; i++)
8617 /* Calculate rmsd */
8618 IntGroupIteratorReset(&iter);
8620 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8622 ap1 = ATOM_AT_INDEX(ap, in);
8623 TransformVec(&tv, trans, &ap1->r);
8625 w += VecLength2(tv);
8628 IntGroupIteratorRelease(&iter);
8634 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
8636 * Calculate the transform to fit the given group to the set of reference coordinates.
8637 * The reference coordinates ref is given as either a frame number, an array of
8638 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
8639 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
8640 * Return values are the transform (that converts the present coordinates to the
8641 * target coordinates) and root mean square deviation (without weight).
8644 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8648 VALUE gval, rval, wval;
8650 IntGroupIterator iter;
8651 int nn, errno, i, j, in, status;
8653 Double *weights, dval[3];
8656 Data_Get_Struct(self, Molecule, mol);
8657 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8659 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8661 ig = IntGroupFromValue(gval);
8662 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8663 IntGroupRelease(ig);
8664 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8666 ref = (Vector *)calloc(sizeof(Vector), nn);
8667 weights = (Double *)calloc(sizeof(Double), nn);
8668 IntGroupIteratorInit(ig, &iter);
8669 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8670 int fn = NUM2INT(rb_Integer(rval));
8671 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8676 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8677 ap = ATOM_AT_INDEX(mol->atoms, in);
8678 if (fn < ap->nframes)
8679 ref[i] = ap->frames[fn];
8680 else ref[i] = ap->r;
8682 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8683 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8684 if (m->row * m->column < nn * 3) {
8688 for (i = 0; i < nn; i++) {
8689 ref[i].x = m->data[i * 3];
8690 ref[i].y = m->data[i * 3 + 1];
8691 ref[i].z = m->data[i * 3 + 2];
8695 rval = rb_protect(rb_ary_to_ary, rval, &status);
8700 if (RARRAY_LEN(rval) < nn) {
8704 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8705 /* Array of 3*nn numbers */
8706 if (RARRAY_LEN(rval) < nn * 3) {
8710 for (i = 0; i < nn; i++) {
8711 for (j = 0; j < 3; j++) {
8712 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8717 dval[j] = NUM2DBL(aval);
8724 /* Array of nn Vector3Ds or Arrays */
8725 for (i = 0; i < nn; i++) {
8726 aval = (RARRAY_PTR(rval))[i];
8727 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8728 VectorFromValue(aval, &ref[i]);
8730 aval = rb_protect(rb_ary_to_ary, aval, &status);
8735 if (RARRAY_LEN(aval) < 3) {
8740 for (j = 0; j < 3; j++) {
8741 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8746 dval[j] = NUM2DBL(aaval);
8756 /* Use atomic weights */
8757 IntGroupIteratorReset(&iter);
8758 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8759 ap = ATOM_AT_INDEX(mol->atoms, in);
8760 weights[i] = ap->weight;
8763 wval = rb_protect(rb_ary_to_ary, wval, &status);
8768 if (RARRAY_LEN(wval) < nn) {
8772 for (i = 0; i < nn; i++) {
8773 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8778 weights[i] = NUM2DBL(wwval);
8781 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8788 IntGroupIteratorRelease(&iter);
8792 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8793 } else if (errno == 1) {
8794 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8795 } else if (errno == 2) {
8796 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8797 } else if (errno == 3) {
8798 rb_jump_tag(status);
8799 } else if (errno == 4) {
8800 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8801 } else if (errno == 5) {
8802 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8803 } else if (errno == 6) {
8804 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8806 return Qnil; /* Not reached */
8813 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8816 s_Molecule_Display(VALUE self)
8819 Data_Get_Struct(self, Molecule, mol);
8820 if (mol->mview != NULL)
8821 MainViewCallback_display(mol->mview);
8829 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8832 s_Molecule_MakeFront(VALUE self)
8835 Data_Get_Struct(self, Molecule, mol);
8836 if (mol->mview != NULL)
8837 MainViewCallback_makeFront(mol->mview);
8843 * update_enabled? -> bool
8845 * Returns true if screen update is enabled; otherwise no.
8848 s_Molecule_UpdateEnabled(VALUE self)
8851 Data_Get_Struct(self, Molecule, mol);
8852 if (mol->mview != NULL && !mol->mview->freezeScreen)
8859 * update_enabled = bool
8861 * Enable or disable screen update. This is effective for automatic update on modification.
8862 * Explicit call to molecule.display() always updates the screen.
8865 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8868 Data_Get_Struct(self, Molecule, mol);
8869 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8870 if (mol->mview != NULL)
8871 mol->mview->freezeScreen = (val == Qfalse);
8879 * show_unitcell(bool)
8880 * show_unitcell = bool
8882 * Set the flag whether to show the unit cell. If no argument is given, the
8883 * current flag is returned.
8886 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8889 Data_Get_Struct(self, Molecule, mol);
8890 if (mol->mview == NULL)
8893 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8894 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8896 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8902 * show_hydrogens(bool)
8903 * show_hydrogens = bool
8905 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8906 * current flag is returned.
8909 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8912 Data_Get_Struct(self, Molecule, mol);
8913 if (mol->mview == NULL)
8916 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8917 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8919 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8925 * show_dummy_atoms(bool)
8926 * show_dummy_atoms = bool
8928 * Set the flag whether to show the dummy atoms. If no argument is given, the
8929 * current flag is returned.
8932 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8935 Data_Get_Struct(self, Molecule, mol);
8936 if (mol->mview == NULL)
8939 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8940 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8942 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8948 * show_expanded(bool)
8949 * show_expanded = bool
8951 * Set the flag whether to show the expanded atoms. If no argument is given, the
8952 * current flag is returned.
8955 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8958 Data_Get_Struct(self, Molecule, mol);
8959 if (mol->mview == NULL)
8962 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8963 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8965 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8971 * show_ellipsoids(bool)
8972 * show_ellipsoids = bool
8974 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8975 * current flag is returned.
8978 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8981 Data_Get_Struct(self, Molecule, mol);
8982 if (mol->mview == NULL)
8985 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8986 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8988 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8993 * is_atom_visible(index) -> Boolean
8995 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8996 * as well as the molecule attributes (showHydrogens, etc.)
8999 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
9004 Data_Get_Struct(self, Molecule, mol);
9005 idx = s_Molecule_AtomIndexFromValue(mol, ival);
9006 if (idx < 0 || idx >= mol->natoms)
9008 ap = ATOM_AT_INDEX(mol->atoms, idx);
9009 if (mol->mview != NULL) {
9010 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
9012 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
9014 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
9017 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
9022 * show_graphite -> Integer
9023 * show_graphite = Integer
9024 * show_graphite = boolean
9026 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
9027 * number of rings to display for each direction.
9028 * If the argument is boolean, only the show/hide flag is set.
9031 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
9034 Data_Get_Struct(self, Molecule, mol);
9035 if (mol->mview == NULL)
9038 if (argv[0] == Qnil || argv[0] == Qfalse)
9039 mol->mview->showGraphiteFlag = 0;
9040 else if (argv[0] == Qtrue)
9041 mol->mview->showGraphiteFlag = 1;
9043 int n = NUM2INT(rb_Integer(argv[0]));
9045 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
9046 mol->mview->showGraphite = n;
9048 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9050 return INT2NUM(mol->mview->showGraphite);
9055 * show_graphite? -> boolean
9057 * Return whether the graphite is set visible or not.
9060 s_Molecule_ShowGraphiteFlag(VALUE self)
9063 Data_Get_Struct(self, Molecule, mol);
9064 if (mol->mview == NULL)
9066 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
9071 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
9072 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
9073 * show_periodic_image = boolean
9075 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
9076 * set but no visual effects are observed.
9077 * If the argument is boolean, only the show/hide flag is modified.
9080 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
9086 Data_Get_Struct(self, Molecule, mol);
9087 if (mol->mview == NULL)
9089 rb_scan_args(argc, argv, "01", &val);
9091 /* Change current settings */
9092 if (val == Qnil || val == Qfalse)
9093 mol->mview->showPeriodicImageFlag = 0;
9094 else if (val == Qtrue)
9095 mol->mview->showPeriodicImageFlag = 1;
9097 val = rb_ary_to_ary(val);
9098 for (i = 0; i < 6; i++) {
9099 if (i < RARRAY_LEN(val))
9100 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
9102 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
9103 rb_raise(rb_eMolbyError, "bad arguments");
9104 for (i = 0; i < 6; i++)
9105 mol->mview->showPeriodicImage[i] = ival[i];
9107 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9110 for (i = 0; i < 6; i++)
9111 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
9117 * show_periodic_image? -> boolean
9119 * Return whether the periodic images are set to visible or not. This flag is
9120 * independent from the show_periodic_image settings.
9123 s_Molecule_ShowPeriodicImageFlag(VALUE self)
9126 Data_Get_Struct(self, Molecule, mol);
9127 if (mol->mview == NULL)
9129 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
9138 * Set the flag whether to draw the model in line mode. If no argument is given, the
9139 * current flag is returned.
9142 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
9145 Data_Get_Struct(self, Molecule, mol);
9146 if (mol->mview == NULL)
9149 mol->mview->lineMode = (RTEST(argv[0]) != 0);
9150 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9152 return (mol->mview->lineMode ? Qtrue : Qfalse);
9159 * Resize the model drawing to fit in the window.
9162 s_Molecule_ResizeToFit(VALUE self)
9165 Data_Get_Struct(self, Molecule, mol);
9166 if (mol->mview != NULL)
9167 MainView_resizeToFit(mol->mview);
9173 * get_view_rotation -> [[ax, ay, az], angle]
9175 * Get the current rotation for the view. Angle is in degree, not radian.
9178 s_Molecule_GetViewRotation(VALUE self)
9183 Data_Get_Struct(self, Molecule, mol);
9184 if (mol->mview == NULL)
9186 TrackballGetRotate(mol->mview->track, f);
9187 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
9191 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
9196 * get_view_scale -> float
9198 * Get the current scale for the view.
9201 s_Molecule_GetViewScale(VALUE self)
9204 Data_Get_Struct(self, Molecule, mol);
9205 if (mol->mview == NULL)
9207 return rb_float_new(TrackballGetScale(mol->mview->track));
9212 * get_view_center -> Vector
9214 * Get the current center point of the view.
9217 s_Molecule_GetViewCenter(VALUE self)
9222 Data_Get_Struct(self, Molecule, mol);
9223 if (mol->mview == NULL)
9225 TrackballGetTranslate(mol->mview->track, f);
9226 v.x = -f[0] * mol->mview->dimension;
9227 v.y = -f[1] * mol->mview->dimension;
9228 v.z = -f[2] * mol->mview->dimension;
9229 return ValueFromVector(&v);
9234 * set_view_rotation([ax, ay, az], angle) -> self
9236 * Set the current rotation for the view. Angle is in degree, not radian.
9239 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
9244 Data_Get_Struct(self, Molecule, mol);
9245 if (mol->mview == NULL)
9247 VectorFromValue(aval, &v);
9248 if (NormalizeVec(&v, &v))
9249 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
9253 f[0] = -NUM2DBL(rb_Float(angval));
9254 TrackballSetRotate(mol->mview->track, f);
9255 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9261 * set_view_scale(scale) -> self
9263 * Set the current scale for the view.
9266 s_Molecule_SetViewScale(VALUE self, VALUE aval)
9269 Data_Get_Struct(self, Molecule, mol);
9270 if (mol->mview == NULL)
9272 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
9273 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9279 * set_view_center(vec) -> self
9281 * Set the current center point of the view.
9284 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
9289 Data_Get_Struct(self, Molecule, mol);
9290 if (mol->mview == NULL)
9292 VectorFromValue(aval, &v);
9293 f[0] = -v.x / mol->mview->dimension;
9294 f[1] = -v.y / mol->mview->dimension;
9295 f[2] = -v.z / mol->mview->dimension;
9296 TrackballSetTranslate(mol->mview->track, f);
9297 MainViewCallback_setNeedsDisplay(mol->mview, 1);
9303 * set_background_color(red, green, blue)
9305 * Set the background color of the model window.
9308 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
9311 Data_Get_Struct(self, Molecule, mol);
9312 if (mol->mview != NULL) {
9313 VALUE rval, gval, bval;
9314 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
9315 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
9322 * create_graphic(kind, color, points, fill = nil) -> integer
9324 * Create a new graphic object.
9325 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
9326 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
9327 * points: an array of Vectors
9331 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
9337 VALUE kval, cval, pval, fval;
9338 Data_Get_Struct(self, Molecule, mol);
9339 if (mol->mview == NULL)
9340 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9341 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
9342 kval = rb_obj_as_string(kval);
9343 memset(&g, 0, sizeof(g));
9345 p = RSTRING_PTR(kval);
9346 if (strcmp(p, "line") == 0)
9347 g.kind = kMainViewGraphicLine;
9348 else if (strcmp(p, "poly") == 0)
9349 g.kind = kMainViewGraphicPoly;
9350 else if (strcmp(p, "cylinder") == 0)
9351 g.kind = kMainViewGraphicCylinder;
9352 else if (strcmp(p, "cone") == 0)
9353 g.kind = kMainViewGraphicCone;
9354 else if (strcmp(p, "ellipsoid") == 0)
9355 g.kind = kMainViewGraphicEllipsoid;
9356 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
9357 g.closed = (RTEST(fval) ? 1 : 0);
9358 cval = rb_ary_to_ary(cval);
9359 n = RARRAY_LEN(cval);
9360 if (n < 3 || n >= 5)
9361 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
9364 for (i = 0; i < n; i++)
9365 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
9366 pval = rb_ary_to_ary(pval);
9367 n = RARRAY_LEN(pval);
9368 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
9370 rb_raise(rb_eArgError, "no control points are given");
9372 case kMainViewGraphicLine:
9374 rb_raise(rb_eArgError, "the line object must have at least two control points");
9376 case kMainViewGraphicPoly:
9378 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
9380 case kMainViewGraphicCylinder:
9381 case kMainViewGraphicCone:
9383 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
9386 case kMainViewGraphicEllipsoid:
9390 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
9393 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
9394 for (i = 0; i < n; i++) {
9397 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
9400 VectorFromValue(RARRAY_PTR(pval)[i], &v);
9402 g.points[i * 3] = v.x;
9403 g.points[i * 3 + 1] = v.y;
9404 g.points[i * 3 + 2] = v.z;
9406 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
9408 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
9409 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
9410 g.points[7] = g.points[11] = g.points[3];
9412 MainView_insertGraphic(mol->mview, -1, &g);
9413 return INT2NUM(mol->mview->ngraphics - 1);
9418 * remove_graphic(index) -> integer
9420 * Remove a graphic object.
9423 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
9427 Data_Get_Struct(self, Molecule, mol);
9428 if (mol->mview == NULL)
9429 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9430 i = NUM2INT(rb_Integer(ival));
9431 if (i < 0 || i >= mol->mview->ngraphics)
9432 rb_raise(rb_eArgError, "graphic index is out of range");
9433 MainView_removeGraphic(mol->mview, i);
9439 * ngraphics -> integer
9441 * Get the number of graphic objects.
9444 s_Molecule_NGraphics(VALUE self)
9447 Data_Get_Struct(self, Molecule, mol);
9448 if (mol->mview == NULL)
9449 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9450 return INT2NUM(mol->mview->ngraphics);
9455 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
9457 * Change the point_index-th control point of graphic_index-th graphic object
9461 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
9463 MainViewGraphic *gp;
9467 Data_Get_Struct(self, Molecule, mol);
9468 if (mol->mview == NULL)
9469 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9470 index = NUM2INT(rb_Integer(gval));
9471 if (index < 0 || index >= mol->mview->ngraphics)
9472 rb_raise(rb_eArgError, "the graphic index is out of range");
9473 gp = mol->mview->graphics + index;
9474 index = NUM2INT(rb_Integer(pval));
9475 if (index < 0 || index >= gp->npoints)
9476 rb_raise(rb_eArgError, "the point index is out of range");
9477 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
9478 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
9479 v.x = NUM2DBL(rb_Float(nval));
9481 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
9482 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
9483 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
9485 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
9488 v.x = kInvalidFloat;
9490 } else VectorFromValue(nval, &v);
9492 gp->points[index * 3] = v.x;
9493 gp->points[index * 3 + 1] = v.y;
9494 gp->points[index * 3 + 2] = v.z;
9495 MoleculeCallback_notifyModification(mol, 0);
9501 * set_graphic_color(graphic_index, new_value) -> new_value
9503 * Change the color of graphic_index-th graphic object
9507 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
9509 MainViewGraphic *gp;
9512 Data_Get_Struct(self, Molecule, mol);
9513 if (mol->mview == NULL)
9514 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9515 index = NUM2INT(rb_Integer(gval));
9516 if (index < 0 || index >= mol->mview->ngraphics)
9517 rb_raise(rb_eArgError, "the graphic index is out of range");
9518 gp = mol->mview->graphics + index;
9519 cval = rb_ary_to_ary(cval);
9520 n = RARRAY_LEN(cval);
9521 if (n != 3 && n != 4)
9522 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
9523 for (index = 0; index < n; index++) {
9524 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
9528 MoleculeCallback_notifyModification(mol, 0);
9534 * show_graphic(graphic_index) -> self
9536 * Enable the visible flag of the graphic_index-th graphic object
9540 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
9542 MainViewGraphic *gp;
9545 Data_Get_Struct(self, Molecule, mol);
9546 if (mol->mview == NULL)
9547 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9548 index = NUM2INT(rb_Integer(gval));
9549 if (index < 0 || index >= mol->mview->ngraphics)
9550 rb_raise(rb_eArgError, "the graphic index is out of range");
9551 gp = mol->mview->graphics + index;
9553 MoleculeCallback_notifyModification(mol, 0);
9559 * hide_graphic(graphic_index) -> self
9561 * Disable the visible flag of the graphic_index-th graphic object
9565 s_Molecule_HideGraphic(VALUE self, VALUE gval)
9567 MainViewGraphic *gp;
9570 Data_Get_Struct(self, Molecule, mol);
9571 if (mol->mview == NULL)
9572 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
9573 index = NUM2INT(rb_Integer(gval));
9574 if (index < 0 || index >= mol->mview->ngraphics)
9575 rb_raise(rb_eArgError, "the graphic index is out of range");
9576 gp = mol->mview->graphics + index;
9578 MoleculeCallback_notifyModification(mol, 0);
9586 * Show the string in the info text box.
9589 s_Molecule_ShowText(VALUE self, VALUE arg)
9592 Data_Get_Struct(self, Molecule, mol);
9593 if (mol->mview != NULL)
9594 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
9600 * md_arena -> MDArena
9602 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
9603 * this molecule, a new arena is created.
9606 s_Molecule_MDArena(VALUE self)
9610 Data_Get_Struct(self, Molecule, mol);
9611 if (mol->arena == NULL)
9613 retval = ValueFromMDArena(mol->arena);
9619 * set_parameter_attr(type, index, key, value, src) -> value
9621 * This method is used only internally.
9624 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
9626 /* This method is called from MolAction to change a MM parameter attribute. */
9631 Data_Get_Struct(self, Molecule, mol);
9632 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
9633 vval = s_ParameterRef_SetAttr(pval, kval, vval);
9635 /* This is the special part of this method; it allows modification of the src field. */
9636 /* (ParameterRef#set_attr sets 0 to the src field) */
9637 Data_Get_Struct(pval, ParameterRef, pref);
9638 up = ParameterRefGetPar(pref);
9639 up->bond.src = FIX2INT(sval);
9646 * parameter -> Parameter
9648 * Get the local parameter of this molecule. If not defined, returns nil.
9651 s_Molecule_Parameter(VALUE self)
9654 Data_Get_Struct(self, Molecule, mol);
9655 /* if (mol->par == NULL)
9657 return s_NewParameterValueFromValue(self);
9662 * selectedMO -> IntGroup
9664 * Returns a group of selected mo in the "MO Info" table. If the MO info table
9665 * is not selected, returns nil. If the MO info table is selected but no MOs
9666 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9669 s_Molecule_SelectedMO(VALUE self)
9674 Data_Get_Struct(self, Molecule, mol);
9675 if (mol->mview == NULL)
9677 ig = MainView_selectedMO(mol->mview);
9680 IntGroupOffset(ig, 1);
9681 val = ValueFromIntGroup(ig);
9682 IntGroupRelease(ig);
9688 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9690 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9691 * If the molecule does not contain a basis set information, then returns nil.
9694 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9697 Vector o, dx, dy, dz;
9700 Int npoints = 80 * 80 * 80;
9701 Data_Get_Struct(self, Molecule, mol);
9702 if (mol->bset == NULL)
9704 rb_scan_args(argc, argv, "01", &nval);
9706 npoints = NUM2INT(rb_Integer(nval));
9707 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9709 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));
9713 s_Cubegen_callback(double progress, void *ref)
9715 MyAppCallback_setProgressValue(progress);
9716 if (MyAppCallback_checkInterrupt())
9723 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9724 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9726 * Calculate the molecular orbital with number mo and create a 'cube' file.
9727 * In the first form, the cube size is estimated from the atomic coordinates. In the
9728 * second form, the cube dimension is explicitly given.
9729 * Returns fname when successful, nil otherwise.
9730 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9731 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9732 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9735 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9737 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9739 Int mono, nx, ny, nz, npoints;
9740 Vector o, dx, dy, dz;
9743 Data_Get_Struct(self, Molecule, mol);
9744 if (mol->bset == NULL)
9745 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9746 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9748 /* Set up parameters */
9749 mono = NUM2INT(rb_Integer(mval));
9750 if (mono <= 0 || mono > mol->bset->ncomps)
9751 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9753 if (mol->bset->rflag != 0)
9754 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9755 mono += mol->bset->ncomps;
9758 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9759 /* Automatic grid formation */
9761 npoints = NUM2INT(rb_Integer(oval));
9765 else if (npoints < 8)
9766 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9767 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9768 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9772 VectorFromValue(oval, &o);
9773 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9774 VectorFromValue(dxval, &dx);
9776 dx.x = NUM2DBL(rb_Float(dxval));
9779 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9780 VectorFromValue(dyval, &dy);
9782 dy.y = NUM2DBL(rb_Float(dyval));
9785 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9786 VectorFromValue(dzval, &dz);
9788 dz.z = NUM2DBL(rb_Float(dzval));
9791 nx = NUM2INT(rb_Integer(nxval));
9792 ny = NUM2INT(rb_Integer(nyval));
9793 nz = NUM2INT(rb_Integer(nzval));
9794 if (nx <= 0 || ny <= 0 || nz <= 0)
9795 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9796 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9797 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);
9801 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9805 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9807 /* Output to file */
9808 MoleculeCallback_displayName(mol, buf, sizeof buf);
9809 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9811 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9813 /* Discard the cube */
9814 MoleculeClearCubeAtIndex(mol, index);
9822 * Get the number of electrostatic potential info.
9825 s_Molecule_NElpots(VALUE self)
9828 Data_Get_Struct(self, Molecule, mol);
9829 return INT2NUM(mol->nelpots);
9836 * Get the electrostatic potential info at the given index. If present, then the
9837 * return value is [Vector, Float] (position and potential). If not present, then
9841 s_Molecule_Elpot(VALUE self, VALUE ival)
9845 Data_Get_Struct(self, Molecule, mol);
9846 idx = NUM2INT(rb_Integer(ival));
9847 if (idx < 0 || idx >= mol->nelpots)
9849 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9854 * add_gaussian_orbital_shell(sym, nprims, atom_index)
9856 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9857 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9861 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9864 int sym, nprims, a_idx, n;
9865 Data_Get_Struct(self, Molecule, mol);
9866 sym = NUM2INT(rb_Integer(symval));
9867 nprims = NUM2INT(rb_Integer(npval));
9868 a_idx = NUM2INT(rb_Integer(aval));
9869 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9871 rb_raise(rb_eMolbyError, "Molecule is emptry");
9873 rb_raise(rb_eMolbyError, "Low memory");
9875 rb_raise(rb_eMolbyError, "Unknown orbital type");
9877 rb_raise(rb_eMolbyError, "Unknown error");
9883 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9885 * To be used internally. Add a gaussian primitive coefficients.
9888 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9892 Double exponent, contraction, contraction_sp;
9893 Data_Get_Struct(self, Molecule, mol);
9894 exponent = NUM2DBL(rb_Float(expval));
9895 contraction = NUM2DBL(rb_Float(cval));
9896 contraction_sp = NUM2DBL(rb_Float(cspval));
9897 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9899 rb_raise(rb_eMolbyError, "Molecule is emptry");
9901 rb_raise(rb_eMolbyError, "Low memory");
9903 rb_raise(rb_eMolbyError, "Unknown error");
9911 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9914 s_Molecule_MOType(VALUE self)
9917 Data_Get_Struct(self, Molecule, mol);
9918 if (mol != NULL && mol->bset != NULL) {
9920 int rflag = mol->bset->rflag;
9923 else if (rflag == 2)
9926 return rb_str_new2(s);
9932 * set_mo_coefficients(idx, energy, coefficients)
9934 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
9935 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9936 * of MO coefficients.
9939 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9945 Data_Get_Struct(self, Molecule, mol);
9946 idx = NUM2INT(rb_Integer(ival));
9947 energy = NUM2DBL(rb_Float(eval));
9948 aval = rb_ary_to_ary(aval);
9949 ncomps = RARRAY_LEN(aval);
9950 coeffs = (Double *)calloc(sizeof(Double), ncomps);
9951 if (coeffs == NULL) {
9955 for (i = 0; i < ncomps; i++)
9956 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9957 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9960 rb_raise(rb_eMolbyError, "Molecule is emptry");
9962 rb_raise(rb_eMolbyError, "Low memory");
9964 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9966 rb_raise(rb_eMolbyError, "Bad MO index");
9968 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9970 rb_raise(rb_eMolbyError, "Unknown error");
9976 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9978 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9979 * ne_alpha, ne_beta: number of alpha/beta electrons.
9982 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9985 Int rflag, na, nb, n;
9986 Data_Get_Struct(self, Molecule, mol);
9987 rflag = NUM2INT(rb_Integer(rval));
9988 na = NUM2INT(rb_Integer(naval));
9989 nb = NUM2INT(rb_Integer(nbval));
9990 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9992 rb_raise(rb_eMolbyError, "Molecule is emptry");
9994 rb_raise(rb_eMolbyError, "Low memory");
9996 rb_raise(rb_eMolbyError, "Unknown error");
10002 * search_equivalent_atoms(ig = nil)
10004 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
10007 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
10013 Data_Get_Struct(self, Molecule, mol);
10014 if (mol->natoms == 0)
10016 rb_scan_args(argc, argv, "01", &val);
10018 ig = IntGroupFromValue(val);
10020 result = MoleculeSearchEquivalentAtoms(mol, ig);
10021 if (result == NULL)
10022 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
10024 IntGroupRelease(ig);
10025 val = rb_ary_new2(mol->natoms);
10026 for (i = 0; i < mol->natoms; i++)
10027 rb_ary_push(val, INT2NUM(result[i]));
10034 * create_pi_anchor(name, group [, type] [, weights] [, index]) -> AtomRef
10036 * Create a pi anchor, which is a "dummy" atom to represent pi-metal bonds.
10037 * Name is the name of the new pi anchor, and group is the atoms that define
10038 * the pi system. Type (a String) is an atom type for MM implementation.
10039 * Weights represent the relative significance of the component atoms; if omitted, then
10040 * 1.0/n (n is the number of component atoms) is assumed for all atoms.
10041 * The weight values will be normalized so that the sum of the weights is 1.0.
10042 * The weight values must be positive.
10043 * Index is the atom index where the created pi-anchor is inserted in the
10044 * atoms array; if omitted, the pi-anchor is inserted after the component atom
10045 * having the largest index.
10046 * Pi anchors are appear in the atom list along with other ordinary atoms. The list
10047 * of component atoms (and their weights) can be retrived by AtomRef#anchor_list.
10050 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
10055 Int i, n, idx, last_component;
10059 if (argc < 2 || argc >= 6)
10060 rb_raise(rb_eMolbyError, "too %s arguments (should be 2..5)", (argc < 2 ? "few" : "many"));
10064 Data_Get_Struct(self, Molecule, mol);
10065 ig = IntGroupFromValue(gval);
10066 memset(&a, 0, sizeof(a));
10067 memset(&an, 0, sizeof(an));
10068 strncpy(a.aname, StringValuePtr(nval), 4);
10069 if (a.aname[0] == '_')
10070 rb_raise(rb_eMolbyError, "please avoid a name beginning with '_' for a pi anchor, because it causes some internal confusion.");
10071 a.type = AtomTypeEncodeToUInt("##"); /* Default atom type for pi_anchor */
10072 for (i = 0; (n = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
10073 if (n >= mol->natoms) {
10074 AtomConnectResize(&an.connect, 0);
10075 rb_raise(rb_eMolbyError, "atom index (%d) out of range", n);
10077 AtomConnectInsertEntry(&an.connect, an.connect.count, n);
10078 last_component = n;
10080 if (an.connect.count == 0)
10081 rb_raise(rb_eMolbyError, "no atoms are specified");
10082 NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), an.connect.count);
10083 for (i = 0; i < an.connect.count; i++) {
10084 an.coeffs[i] = 1.0 / an.connect.count;
10086 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_cString))) {
10088 if (argv[0] != Qnil)
10089 a.type = AtomTypeEncodeToUInt(StringValuePtr(argv[0]));
10093 if (argc > 0 && (argv[0] == Qnil || rb_obj_is_kind_of(argv[0], rb_mEnumerable))) {
10094 if (argv[0] != Qnil) {
10095 VALUE aval = rb_ary_to_ary(argv[0]);
10097 if (RARRAY_LEN(aval) != an.connect.count)
10098 rb_raise(rb_eMolbyError, "the number of weight values does not match the number of atoms");
10099 for (i = 0, sum = 0.0; i < an.connect.count; i++) {
10100 d = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
10102 rb_raise(rb_eMolbyError, "the weight value must be positive");
10106 for (i = 0; i < an.connect.count; i++)
10107 an.coeffs[i] /= sum;
10112 if (argc > 0 && argv[0] != Qnil) {
10114 idx = NUM2INT(rb_Integer(argv[0]));
10116 if (idx < 0 || idx > mol->natoms) {
10117 /* Immediately after the last specified atom */
10118 idx = last_component + 1;
10120 a.anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
10121 memmove(a.anchor, &an, sizeof(PiAnchor));
10122 /* Use residue information of the last specified atom */
10123 ap = ATOM_AT_INDEX(mol->atoms, last_component);
10124 a.resSeq = ap->resSeq;
10125 strncpy(a.resName, ap->resName, 4);
10126 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &a, idx, &idx) != 0)
10128 MoleculeCalculatePiAnchorPosition(mol, idx);
10129 aref = AtomRefNew(mol, idx);
10130 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
10135 * current -> Molecule
10137 * Get the currently "active" molecule.
10140 s_Molecule_Current(VALUE klass)
10142 return ValueFromMolecule(MoleculeCallback_currentMolecule());
10147 * Molecule[] -> Molecule
10148 * Molecule[n] -> Molecule
10149 * Molecule[name] -> Molecule
10150 * Molecule[name, k] -> Molecule
10151 * Molecule[regex] -> Molecule
10152 * Molecule[regex, k] -> Molecule
10154 * Molecule[] is equivalent to Molecule.current.
10155 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
10156 * Molecule[name] gives the first document (in the order of creation time) that has
10157 * the given name. If a second argument (k) is given, the k-th document that has the
10158 * given name is returned.
10159 * Molecule[regex] gives the first document (in the order of creation time) that
10160 * has a name matching the regular expression. If a second argument (k) is given,
10161 * the k-th document that has a name matching the re is returned.
10164 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
10170 rb_scan_args(argc, argv, "02", &val, &kval);
10172 return s_Molecule_Current(klass);
10173 if (rb_obj_is_kind_of(val, rb_cInteger)) {
10174 idx = NUM2INT(val);
10175 mol = MoleculeCallback_moleculeAtIndex(idx);
10176 } else if (rb_obj_is_kind_of(val, rb_cString)) {
10177 char *p = StringValuePtr(val);
10178 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10179 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10180 MoleculeCallback_displayName(mol, buf, sizeof buf);
10181 if (strcmp(buf, p) == 0 && --k == 0)
10184 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
10185 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
10186 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
10188 MoleculeCallback_displayName(mol, buf, sizeof buf);
10189 name = rb_str_new2(buf);
10190 if (rb_reg_match(val, name) != Qnil && --k == 0)
10193 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
10197 else return ValueFromMolecule(mol);
10202 * list -> array of Molecules
10204 * Get the list of molecules associated to the documents, in the order of creation
10205 * time of the document. If no document is open, returns an empry array.
10208 s_Molecule_List(VALUE klass)
10214 ary = rb_ary_new();
10215 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
10216 rb_ary_push(ary, ValueFromMolecule(mol));
10224 * ordered_list -> array of Molecules
10226 * Get the list of molecules associated to the documents, in the order of front-to-back
10227 * ordering of the associated window. If no document is open, returns an empry array.
10230 s_Molecule_OrderedList(VALUE klass)
10236 ary = rb_ary_new();
10237 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
10238 rb_ary_push(ary, ValueFromMolecule(mol));
10246 * error_message -> String
10248 * Get the error_message from the last load/save method. If no error, returns nil.
10251 s_Molecule_ErrorMessage(VALUE klass)
10253 if (gLoadSaveErrorMessage == NULL)
10255 else return rb_str_new2(gLoadSaveErrorMessage);
10260 * set_error_message(String)
10261 * Molecule.error_message = String
10263 * Get the error_message from the last load/save method. If no error, returns nil.
10266 s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
10268 if (gLoadSaveErrorMessage != NULL) {
10269 free(gLoadSaveErrorMessage);
10270 gLoadSaveErrorMessage = NULL;
10272 if (sval != Qnil) {
10273 sval = rb_str_to_str(sval);
10274 gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
10281 * self == Molecule -> boolean
10283 * True if the two arguments point to the same molecule.
10286 s_Molecule_Equal(VALUE self, VALUE val)
10288 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
10289 Molecule *mol1, *mol2;
10290 Data_Get_Struct(self, Molecule, mol1);
10291 Data_Get_Struct(val, Molecule, mol2);
10292 return (mol1 == mol2 ? Qtrue : Qfalse);
10293 } else return Qfalse;
10296 /* The callback functions for call_subprocess_async */
10298 s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
10301 VALUE procval, retval, args[2];
10302 args[0] = ValueFromMolecule(mol);
10303 args[1] = INT2NUM(status);
10304 procval = rb_ivar_get(args[0], rb_intern("end_proc"));
10305 if (procval != Qnil) {
10306 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10307 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10314 s_Molecule_CallSubProcessAsync_TimerCallback(Molecule *mol, int tcount)
10317 VALUE procval, retval, args[2];
10318 args[0] = ValueFromMolecule(mol);
10319 args[1] = INT2NUM(tcount);
10320 procval = rb_ivar_get(args[0], rb_intern("timer_proc"));
10321 if (procval != Qnil) {
10322 retval = Ruby_funcall2_protect(procval, rb_intern("call"), 2, args, &ruby_status);
10323 if (ruby_status != 0 || retval == Qnil || retval == Qfalse)
10331 * call_subprocess_async(cmd [, end_callback [, timer_callback [, standard_output_file [, error_output_file]]]])
10333 * Call subprocess asynchronically.
10334 * If end_callback is given, it will be called (with two arguments self and termination status)
10335 * when the subprocess terminated.
10336 * If timer_callback is given, it will be called (also with two arguments, self and timer count).
10337 * If timer_callback returns nil or false, then the subprocess will be interrupted.
10338 * If stdout_file or stderr_file is a filename, then the message will be sent to the file; if the
10339 * filename begins with ">>", then the message will be appended to the file.
10340 * If the filename is "/dev/null" or "NUL", then the message will be lost.
10341 * If the argument is nil, then the message will be sent to the Ruby console.
10342 * Returns the process ID as an integer.
10345 s_Molecule_CallSubProcessAsync(int argc, VALUE *argv, VALUE self)
10347 VALUE cmd, end_proc, timer_proc, stdout_val, stderr_val;
10351 FILE *fpout, *fperr;
10352 rb_scan_args(argc, argv, "14", &cmd, &end_proc, &timer_proc, &stdout_val, &stderr_val);
10353 Data_Get_Struct(self, Molecule, mol);
10355 if (stdout_val == Qnil) {
10358 sout = StringValuePtr(stdout_val);
10359 if (strcmp(sout, "/dev/null") == 0 || strcmp(sout, "NUL") == 0)
10362 if (strncmp(sout, ">>", 2) == 0) {
10364 fpout = fopen(sout, "a");
10368 fpout = fopen(sout, "w");
10371 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", sout);
10374 if (stderr_val == Qnil) {
10377 serr = StringValuePtr(stderr_val);
10378 if (strcmp(serr, "/dev/null") == 0 || strcmp(serr, "NUL") == 0)
10381 if (strncmp(serr, ">>", 2) == 0) {
10383 fpout = fopen(serr, "a");
10387 fperr = fopen(serr, "w");
10390 rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
10394 /* Register procs as instance variables */
10395 rb_ivar_set(self, rb_intern("end_proc"), end_proc);
10396 rb_ivar_set(self, rb_intern("timer_proc"), timer_proc);
10397 n = MoleculeCallback_callSubProcessAsync(mol, StringValuePtr(cmd), s_Molecule_CallSubProcessAsync_EndCallback, (timer_proc == Qnil ? NULL : s_Molecule_CallSubProcessAsync_TimerCallback), fpout, fperr);
10398 if (fpout != NULL && fpout != (FILE *)1)
10400 if (fperr != NULL && fperr != (FILE *)1)
10410 /* Define module Molby */
10411 rb_mMolby = rb_define_module("Molby");
10413 /* Define Vector3D, Transform, IntGroup */
10416 /* Define MDArena */
10417 Init_MolbyMDTypes();
10419 /* class Molecule */
10420 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
10421 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
10422 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
10423 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
10424 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
10425 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
10426 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
10427 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
10428 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
10429 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
10430 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
10431 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
10432 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
10433 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
10434 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
10435 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
10436 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
10437 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
10438 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
10439 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
10440 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
10441 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
10442 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
10443 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
10444 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
10445 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
10446 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
10447 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
10448 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
10449 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
10450 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
10451 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
10452 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
10453 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
10454 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
10455 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
10456 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
10457 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
10459 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
10460 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
10461 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
10462 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
10463 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
10465 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
10466 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
10467 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
10468 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
10469 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
10470 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
10472 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
10473 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
10474 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
10475 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
10476 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
10477 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
10478 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
10479 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
10480 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
10481 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
10482 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
10483 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
10484 rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
10485 rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
10486 rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
10487 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
10488 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
10489 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
10490 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
10491 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
10492 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
10493 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
10494 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
10495 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
10496 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
10497 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
10498 rb_define_alias(rb_cMolecule, "+", "add");
10499 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
10500 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
10501 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
10502 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
10503 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
10504 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
10505 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
10506 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
10507 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
10508 rb_define_method(rb_cMolecule, "assign_bond_order", s_Molecule_AssignBondOrder, 2);
10509 rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
10510 rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
10511 rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
10512 rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
10513 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
10514 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
10515 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
10516 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
10517 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
10518 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, -1);
10519 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
10520 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
10521 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
10522 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
10523 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
10524 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
10525 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
10526 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
10527 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
10528 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
10529 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
10530 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
10531 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
10532 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
10533 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
10534 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
10535 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
10536 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
10537 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
10538 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
10539 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
10540 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
10541 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
10542 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
10543 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
10544 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
10545 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
10546 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
10547 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
10548 rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
10549 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
10550 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
10551 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
10552 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
10553 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
10554 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
10555 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
10556 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
10557 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
10558 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
10559 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
10560 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
10561 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
10562 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
10563 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
10564 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
10565 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
10566 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
10567 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
10568 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
10569 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
10570 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
10571 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
10572 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
10573 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
10574 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
10575 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
10576 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
10577 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
10578 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
10579 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
10580 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
10581 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
10582 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
10583 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
10584 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
10585 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
10586 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
10587 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
10588 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
10589 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
10590 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
10591 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
10592 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
10593 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
10594 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
10595 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
10596 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
10597 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
10598 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
10599 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
10600 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
10601 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
10602 #if 1 || !defined(__CMDMAC__)
10603 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
10604 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
10605 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
10606 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
10607 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
10608 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
10609 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
10610 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
10611 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
10612 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
10613 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
10614 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
10615 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10616 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10618 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10619 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10620 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10621 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10622 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10623 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10624 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10625 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10626 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10627 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10628 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10629 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10630 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10631 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10632 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10634 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10635 rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
10636 rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
10638 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10639 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10640 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10641 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10642 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10643 rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
10644 rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
10645 rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
10647 /* class MolEnumerable */
10648 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10649 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10650 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10651 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10652 rb_define_alias(rb_cMolEnumerable, "size", "length");
10653 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10654 rb_define_method(rb_cMolEnumerable, "==", s_MolEnumerable_Equal, 1);
10656 /* class AtomRef */
10657 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10658 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10660 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10661 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10662 s_AtomAttrDefTable[i].id = rb_intern(buf);
10663 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10665 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10667 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10668 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10669 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10670 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10671 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10672 rb_global_variable(&s_SetAtomAttrString);
10673 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10674 rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
10676 /* class Parameter */
10677 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10678 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10679 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10680 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10681 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10682 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10683 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10684 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10685 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10686 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10687 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10688 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10689 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10690 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10691 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10692 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10693 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10694 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10695 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10696 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10697 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10698 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10699 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10700 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10701 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10702 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10703 rb_define_method(rb_cParameter, "==", s_Parameter_Equal, 1);
10704 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10705 rb_define_singleton_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10706 rb_define_singleton_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10707 rb_define_singleton_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10708 rb_define_singleton_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10709 rb_define_singleton_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10710 rb_define_singleton_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10711 rb_define_singleton_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10712 rb_define_singleton_method(rb_cParameter, "element", s_Parameter_Element, 1);
10713 rb_define_singleton_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10714 rb_define_singleton_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10715 rb_define_singleton_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10716 rb_define_singleton_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10717 rb_define_singleton_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10718 rb_define_singleton_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10719 rb_define_singleton_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10720 rb_define_singleton_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10721 rb_define_singleton_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10722 rb_define_singleton_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10723 rb_define_singleton_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10724 rb_define_singleton_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10725 rb_define_singleton_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10726 rb_define_singleton_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10727 rb_define_singleton_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10728 rb_define_singleton_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10729 rb_define_singleton_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10730 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10732 /* class ParEnumerable */
10733 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10734 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10735 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10736 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10737 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10738 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10739 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10740 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10741 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10742 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10743 rb_define_method(rb_cParEnumerable, "==", s_ParEnumerable_Equal, 1);
10745 /* class ParameterRef */
10746 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10747 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10749 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10750 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10751 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10752 if (s_ParameterAttrDefTable[i].symref != NULL)
10753 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10754 if (s_ParameterAttrDefTable[i].setter != NULL) {
10756 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10759 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10760 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10761 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10762 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10763 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10764 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10765 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10766 rb_define_method(rb_cParameterRef, "==", s_ParameterRef_Equal, 1);
10768 /* class MolbyError */
10769 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10771 /* module Kernel */
10772 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10773 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10774 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10775 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10776 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10777 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10778 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10779 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10780 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10781 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10782 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10783 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10784 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10785 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10786 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, -1);
10787 rb_define_method(rb_mKernel, "backquote", s_Kernel_Backquote, 1);
10788 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10789 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10790 rb_define_method(rb_mKernel, "show_console_window", s_Kernel_ShowConsoleWindow, 0);
10791 rb_define_method(rb_mKernel, "hide_console_window", s_Kernel_HideConsoleWindow, 0);
10792 rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
10793 rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
10794 rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
10795 rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
10797 s_ID_equal = rb_intern("==");
10798 g_RubyID_call = rb_intern("call");
10801 #pragma mark ====== External functions ======
10803 static VALUE s_ruby_top_self = Qfalse;
10804 static VALUE s_ruby_get_binding_for_molecule = Qfalse;
10805 static VALUE s_ruby_export_local_variables = Qfalse;
10808 s_evalRubyScriptOnMoleculeSub(VALUE val)
10810 void **ptr = (void **)val;
10811 Molecule *mol = (Molecule *)ptr[1];
10812 VALUE sval, fnval, lnval, retval;
10815 if (s_ruby_top_self == Qfalse) {
10816 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10818 if (s_ruby_get_binding_for_molecule == Qfalse) {
10820 "proc { |_mol_, _bind_| \n"
10821 " _proc_ = eval(\"proc { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
10822 " _proc_.call(_mol_) } ";
10823 s_ruby_get_binding_for_molecule = rb_eval_string(s1);
10824 rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
10826 if (s_ruby_export_local_variables == Qfalse) {
10828 "proc { |_bind_| \n"
10829 " # find local variables newly defined in _bind_ \n"
10830 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
10831 " _a_.each { |_vsym_| \n"
10832 " _vname_ = _vsym_.to_s \n"
10833 " _vval_ = _bind_.eval(_vname_) \n"
10834 " # Define local variable \n"
10835 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
10836 " # Then set value \n"
10837 " TOPLEVEL_BINDING.eval(\"proc { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
10840 s_ruby_export_local_variables = rb_eval_string(s2);
10841 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
10843 if (ptr[2] == NULL) {
10845 /* String literal: we need to specify string encoding */
10846 asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
10847 sval = rb_str_new2(scr);
10849 fnval = rb_str_new2("(eval)");
10850 lnval = INT2FIX(0);
10852 sval = rb_str_new2((char *)ptr[0]);
10853 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10854 lnval = INT2FIX(1);
10856 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
10858 VALUE mval = ValueFromMolecule(mol);
10859 binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
10861 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
10863 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
10869 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10873 VALUE save_interrupt_flag;
10874 /* char *save_ruby_sourcefile;
10875 int save_ruby_sourceline; */
10876 if (gMolbyIsCheckingInterrupt) {
10877 MolActionAlertRubyIsRunning();
10879 return (RubyValue)Qnil;
10882 args[0] = (void *)script;
10883 args[1] = (void *)mol;
10884 args[2] = (void *)fname;
10885 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10886 /* save_ruby_sourcefile = ruby_sourcefile;
10887 save_ruby_sourceline = ruby_sourceline; */
10888 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10889 if (*status != 0) {
10890 /* Is this 'exit' exception? */
10891 VALUE last_exception = rb_gv_get("$!");
10892 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
10893 /* Capture exit and return the status value */
10894 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
10898 s_SetInterruptFlag(Qnil, save_interrupt_flag);
10899 /* ruby_sourcefile = save_ruby_sourcefile;
10900 ruby_sourceline = save_ruby_sourceline; */
10906 Molby_showRubyValue(RubyValue value, char **outValueString)
10908 VALUE val = (VALUE)value;
10909 if (gMolbyIsCheckingInterrupt) {
10910 MolActionAlertRubyIsRunning();
10917 val = rb_protect(rb_inspect, val, &status);
10919 str = StringValuePtr(val);
10920 if (outValueString != NULL)
10921 *outValueString = strdup(str);
10922 MyAppCallback_showScriptMessage("%s", str);
10927 Molby_showError(int status)
10929 static const int tag_raise = 6;
10930 char *msg = NULL, *msg2;
10931 VALUE val, backtrace;
10932 int interrupted = 0;
10933 if (status == tag_raise) {
10934 VALUE errinfo = rb_errinfo();
10935 VALUE eclass = CLASS_OF(errinfo);
10936 if (eclass == rb_eInterrupt) {
10942 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10944 val = rb_eval_string_protect("$!.to_s", &status);
10946 msg = RSTRING_PTR(val);
10947 else msg = "(message not available)";
10949 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10950 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10956 Molby_getDescription(void)
10958 extern const char *gVersionString, *gCopyrightString;
10959 extern int gRevisionNumber;
10960 extern char *gLastBuildString;
10962 char *revisionString;
10963 if (gRevisionNumber > 0) {
10964 asprintf(&revisionString, ", revision %d", gRevisionNumber);
10965 } else revisionString = "";
10967 "Molby %s%s\n%s\nLast compile: %s\n"
10968 #if !defined(__CMDMAC__)
10974 "ruby %s, http://www.ruby-lang.org/\n"
10976 "FFTW 3.3.2, http://www.fftw.org/\n"
10977 " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
10978 " and Massachusetts Institute of Technology",
10979 gVersionString, revisionString, gCopyrightString, gLastBuildString,
10980 #if !defined(__CMDMAC__)
10981 MyAppCallback_getGUIDescriptionString(),
10983 gRubyVersion, gRubyCopyright);
10984 if (revisionString[0] != 0)
10985 free(revisionString);
10990 Molby_startup(const char *script, const char *dir)
10995 char *respath, *p, *wbuf;
10997 /* Get version/copyright string from Ruby interpreter */
10999 gRubyVersion = strdup(ruby_version);
11000 asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
11001 #if defined(__CMDMAC__)
11004 " ", /* Indent for displaying in About dialog */
11006 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
11009 /* Read build and revision information for Molby */
11012 extern int gRevisionNumber;
11013 extern char *gLastBuildString;
11014 FILE *fp = fopen("../buildInfo.txt", "r");
11015 gLastBuildString = "";
11017 if (fgets(buf, sizeof(buf), fp) != NULL) {
11018 char *p1 = strchr(buf, '\"');
11019 char *p2 = strrchr(buf, '\"');
11020 if (p1 != NULL && p2 != NULL && p2 - p1 > 1) {
11021 memmove(buf, p1 + 1, p2 - p1 - 1);
11022 buf[p2 - p1 - 1] = 0;
11023 asprintf(&gLastBuildString, "Last compile: %s\n", buf);
11028 fp = fopen("../revisionInfo.txt", "r");
11029 gRevisionNumber = 0;
11031 if (fgets(buf, sizeof(buf), fp) != NULL) {
11032 gRevisionNumber = strtol(buf, NULL, 0);
11038 #if defined(__CMDMAC__)
11039 wbuf = Molby_getDescription();
11040 printf("%s\n", wbuf);
11044 /* Read atom display parameters */
11045 if (ElementParameterInitialize("element.par", &wbuf) != 0) {
11046 #if defined(__CMDMAC__)
11047 fprintf(stderr, "%s\n", wbuf);
11049 MyAppCallback_setConsoleColor(1);
11050 MyAppCallback_showScriptMessage("%s", wbuf);
11051 MyAppCallback_setConsoleColor(0);
11056 /* Read default parameters */
11057 ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
11058 if (wbuf != NULL) {
11059 #if defined(__CMDMAC__)
11060 fprintf(stderr, "%s\n", wbuf);
11062 MyAppCallback_setConsoleColor(1);
11063 MyAppCallback_showScriptMessage("%s", wbuf);
11064 MyAppCallback_setConsoleColor(0);
11069 /* Initialize Ruby interpreter */
11072 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
11074 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
11075 ruby_incpush(libpath);
11079 ruby_script("Molby");
11081 /* Find the resource path (the parent directory of the given directory) */
11082 respath = strdup(dir);
11083 p = strrchr(respath, '/');
11084 if (p == NULL && PATH_SEPARATOR != '/')
11085 p = strrchr(respath, PATH_SEPARATOR);
11088 val = Ruby_NewFileStringValue(respath);
11089 rb_define_global_const("MolbyResourcePath", val);
11092 /* Define Molby classes */
11094 RubyDialogInitClass();
11096 rb_define_const(rb_mMolby, "ResourcePath", val);
11097 val = Ruby_NewFileStringValue(dir);
11098 rb_define_const(rb_mMolby, "ScriptPath", val);
11099 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
11100 val = Ruby_NewFileStringValue(p);
11101 rb_define_const(rb_mMolby, "MbsfPath", val);
11104 #if defined(__CMDMAC__)
11105 rb_define_const(rb_mMolby, "HasGUI", Qfalse);
11107 rb_define_const(rb_mMolby, "HasGUI", Qtrue);
11112 /* Create objects for stdout and stderr */
11113 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11114 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
11115 rb_gv_set("$stdout", val);
11116 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11117 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
11118 rb_gv_set("$stderr", val);
11120 /* Create objects for stdin */
11121 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
11122 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
11123 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
11124 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
11125 rb_gv_set("$stdin", val);
11129 /* Global variable to hold backtrace */
11130 rb_define_variable("$backtrace", &gMolbyBacktrace);
11133 /* Register interrupt check code */
11134 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
11138 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
11139 s_SetIntervalTimer(0, 50);
11142 /* Read the startup script */
11143 if (script != NULL && script[0] != 0) {
11144 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
11146 rb_load_protect(rb_str_new2(script), 0, &status);
11149 Molby_showError(status);
11151 MyAppCallback_showScriptMessage("Done.\n");
11156 Molby_buildARGV(int argc, const char **argv)
11159 rb_ary_clear(rb_argv);
11160 for (i = 0; i < argc; i++) {
11161 VALUE arg = rb_tainted_str_new2(argv[i]);
11163 rb_ary_push(rb_argv, arg);