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"
27 #include <version.h> /* for Ruby version */
28 #include <node.h> /* for rb_add_event_hook() */
30 #if defined(__WXMAC__) || defined(__CMDMAC__)
31 #define USE_PTHREAD_FOR_TIMER 1
35 #if USE_PTHREAD_FOR_TIMER
36 #include <unistd.h> /* for usleep() */
37 #include <pthread.h> /* for pthread */
39 #include <signal.h> /* for sigaction() */
43 #include "../Missing.h"
45 #pragma mark ====== Global Values ======
49 VALUE rb_cMolecule, rb_cMolEnumerable, rb_cAtomRef;
50 VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
52 VALUE gMolbyBacktrace;
54 int gMolbyRunLevel = 0;
55 int gMolbyIsCheckingInterrupt = 0;
57 char *gRubyVersion, *gRubyCopyright;
60 static VALUE s_ID_equal; /* rb_intern("==") */
62 /* Symbols for atom attributes */
64 s_IndexSym, s_SegSeqSym, s_SegNameSym, s_ResSeqSym,
65 s_ResNameSym, s_NameSym, s_AtomTypeSym, s_ChargeSym,
66 s_WeightSym, s_ElementSym, s_AtomicNumberSym, s_ConnectsSym,
67 s_RSym, s_XSym, s_YSym, s_ZSym,
68 s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
69 s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
70 s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
71 s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
72 s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
75 /* Symbols for parameter attributes */
77 s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym,
78 s_A0Sym, s_MultSym, s_PeriodSym, s_Phi0Sym,
81 /* s_A14Sym, s_B14Sym, */
82 s_Req14Sym, s_Eps14Sym,
83 s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
84 s_CommentSym, s_SourceSym;
88 * Get ary[i] by calling "[]" method
91 Ruby_ObjectAtIndex(VALUE ary, int idx)
93 static ID index_method = 0;
94 if (TYPE(ary) == T_ARRAY) {
95 int len = RARRAY_LEN(ary);
96 if (idx >= 0 && idx < len)
97 return (RARRAY_PTR(ary))[idx];
100 if (index_method == 0)
101 index_method = rb_intern("[]");
102 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
106 Ruby_FileStringValuePtr(VALUE *valp)
109 char *p = strdup(StringValuePtr(*valp));
110 translate_char(p, '/', '\\');
111 *valp = rb_str_new2(p);
113 return StringValuePtr(*valp);
115 return StringValuePtr(*valp);
120 Ruby_NewFileStringValue(const char *fstr)
124 char *p = strdup(fstr);
125 translate_char(p, '\\', '/');
126 retval = rb_str_new2(p);
130 return rb_str_new2(fstr);
135 Ruby_ObjToStringObj(VALUE val)
141 return rb_str_new2(rb_id2name(SYM2ID(val)));
143 return rb_str_to_str(val);
147 #pragma mark ====== Message input/output ======
151 * message_box(str, title, button = nil, icon = :info)
153 * Show a message box.
154 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
155 * Icon: :info, :warning, :error
158 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
160 char *str, *title, *s;
162 VALUE sval, tval, bval, ival;
163 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
164 str = StringValuePtr(sval);
165 title = StringValuePtr(tval);
167 bval = Ruby_ObjToStringObj(bval);
168 s = RSTRING_PTR(bval);
169 if (strncmp(s, "ok", 2) == 0)
171 else if (strncmp(s, "cancel", 6) == 0)
174 rb_raise(rb_eMolbyError, "the button specification should be either nil, :ok or :cancel");
177 ival = Ruby_ObjToStringObj(ival);
178 s = RSTRING_PTR(ival);
179 if (strncmp(s, "info", 4) == 0)
181 else if (strncmp(s, "warn", 4) == 0)
183 else if (strncmp(s, "err", 3) == 0)
186 rb_raise(rb_eMolbyError, "the icon specification should be either :info, :warning or :error");
188 MyAppCallback_messageBox(str, title, buttons, icon);
194 * error_message_box(str)
196 * Show an error message box.
199 s_Kernel_ErrorMessageBox(VALUE self, VALUE sval)
201 char *str = StringValuePtr(sval);
202 MyAppCallback_errorMessageBox("%s", str);
208 * ask(prompt, default = nil) -> string
210 * Open a modal dialog and get a line of text.
213 s_Kernel_Ask(int argc, VALUE *argv, VALUE self)
215 volatile VALUE prompt, message;
218 rb_scan_args(argc, argv, "11", &prompt, &message);
219 if (message != Qnil) {
220 strncpy(buf, StringValuePtr(message), sizeof buf - 1);
221 buf[sizeof buf - 1] = 0;
223 retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
225 return rb_str_new2(buf);
234 * Put the message in the main text view in black color.
237 s_StandardOutput(VALUE self, VALUE str)
240 MyAppCallback_setConsoleColor(0);
241 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
249 * Put the message in the main text view in red color.
252 s_StandardErrorOutput(VALUE self, VALUE str)
255 MyAppCallback_setConsoleColor(1);
256 n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
257 MyAppCallback_setConsoleColor(0);
263 * stdin.gets(rs = $/)
265 * Read one line message via dialog box.
268 s_StandardInputGets(int argc, VALUE *argv, VALUE self)
271 pval = rb_str_new2("Enter a line:");
272 rval = s_Kernel_Ask(1, &pval, self);
275 rb_str_cat2(rval, "\n");
281 * stdin.method_missing(name, args, ...)
283 * Throw an exception, noting only gets and readline are defined.
286 s_StandardInputMethodMissing(int argc, VALUE *argv, VALUE self)
289 rb_scan_args(argc, argv, "10", &nval);
290 rb_raise(rb_eMolbyError, "'%s' is undefined. Only 'gets' and 'readline' can be used for stdin within Molby.", rb_id2name(SYM2ID(nval)));
291 return Qnil; /* Not reached */
294 #pragma mark ====== Track key events ======
296 /* User interrupt handling
297 * User interrupt (command-period on Mac OS) is handled by periodic polling of
298 * key events. This polling should only be enabled during "normal" execution
299 * of scripts and must be disabled when the rest of the application (or Ruby
300 * script itself) is handling GUI. This is ensured by appropriate calls to
301 * enable_interrupt and disable_interrupt. */
303 static VALUE s_interrupt_flag = Qfalse;
306 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
308 volatile VALUE message;
310 if (Ruby_GetInterruptFlag() == Qtrue) {
311 rb_scan_args(argc, argv, "01", &message);
313 p = StringValuePtr(message);
316 MyAppCallback_showProgressPanel(p);
322 s_HideProgressPanel(VALUE self)
324 MyAppCallback_hideProgressPanel();
329 s_SetProgressValue(VALUE self, VALUE val)
331 double dval = NUM2DBL(rb_Float(val));
332 MyAppCallback_setProgressValue(dval);
337 s_SetProgressMessage(VALUE self, VALUE msg)
342 else p = StringValuePtr(msg);
343 MyAppCallback_setProgressMessage(p);
348 s_SetInterruptFlag(VALUE self, VALUE val)
352 if (val == Qfalse || val == Qnil)
356 oldval = s_interrupt_flag;
358 s_interrupt_flag = val;
360 s_HideProgressPanel(self);
367 s_GetInterruptFlag(VALUE self)
369 return s_SetInterruptFlag(self, Qundef);
374 s_Ruby_CallMethod(VALUE val)
376 void **ptr = (void **)val;
377 VALUE receiver = (VALUE)ptr[0];
378 ID method_id = (ID)ptr[1];
379 VALUE args = (VALUE)ptr[2];
381 if (method_id == 0) {
382 /* args should be a string, which is evaluated */
383 if (receiver == Qnil) {
384 retval = rb_eval_string(StringValuePtr(args));
386 retval = rb_obj_instance_eval(1, &args, receiver);
389 /* args should be an array of arguments */
390 retval = rb_apply(receiver, method_id, args);
396 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
398 VALUE retval, save_interrupt_flag;
400 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
401 ptr[0] = (void *)receiver;
402 ptr[1] = (void *)method_id;
403 ptr[2] = (void *)args;
404 MyAppCallback_beginUndoGrouping();
405 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
406 MyAppCallback_endUndoGrouping();
407 s_SetInterruptFlag(Qnil, save_interrupt_flag);
408 MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */
\v
414 Ruby_SetInterruptFlag(VALUE val)
416 return s_SetInterruptFlag(Qnil, val);
420 Ruby_GetInterruptFlag(void)
422 return s_SetInterruptFlag(Qnil, Qundef);
427 * check_interrupt -> integer
429 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
432 s_Kernel_CheckInterrupt(VALUE self)
434 if (Ruby_GetInterruptFlag() == Qfalse)
436 else if (MyAppCallback_checkInterrupt())
438 else return INT2NUM(0);
441 static volatile unsigned long sITimerCount = 0;
444 static HANDLE sITimerEvent;
445 static HANDLE sITimerThread;
446 static int sITimerInterval;
448 static __stdcall unsigned
449 s_ITimerThreadFunc(void *p)
451 while (WaitForSingleObject(sITimerEvent, sITimerInterval) == WAIT_TIMEOUT) {
457 #elif USE_PTHREAD_FOR_TIMER
460 static pthread_t sTimerThread;
462 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
463 static volatile signed char sTimerFlag = -1;
464 static volatile int sTimerIntervalMicrosec = 0;
467 s_TimerThreadEntry(void *param)
470 usleep(sTimerIntervalMicrosec);
473 else if (sTimerFlag == -2)
482 s_SignalAction(int n)
488 s_SetIntervalTimer(int n, int msec)
492 /* Start interval timer */
493 sITimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
494 sITimerInterval = msec;
496 sITimerThread = (HANDLE)_beginthreadex(NULL, 0, s_ITimerThreadFunc, NULL, 0, NULL);
499 /* Stop interval timer */
501 SetEvent(sITimerEvent); /* Tell thread to terminate */
503 WaitForSingleObject(sITimerThread, 1000);
504 CloseHandle(sITimerThread);
507 CloseHandle(sITimerEvent);
509 sITimerThread = NULL;
511 #elif USE_PTHREAD_FOR_TIMER
513 if (sTimerFlag == -1) {
514 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
516 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d\n", status);
519 sTimerFlag = 0; /* Active */
520 sTimerIntervalMicrosec = msec * 1000;
521 } else if (sTimerFlag != -1)
522 sTimerFlag = 1; /* Inactive */
524 static struct itimerval sOldValue;
525 static struct sigaction sOldAction;
526 struct itimerval val;
527 struct sigaction act;
530 act.sa_handler = s_SignalAction;
533 sigaction(SIGALRM, &act, &sOldAction);
534 val.it_value.tv_sec = 0;
535 val.it_value.tv_usec = msec * 1000;
536 val.it_interval.tv_sec = 0;
537 val.it_interval.tv_usec = msec * 1000;
538 setitimer(ITIMER_REAL, &val, &sOldValue);
540 setitimer(ITIMER_REAL, &sOldValue, &val);
541 sigaction(SIGALRM, &sOldAction, &act);
547 s_GetTimerCount(void)
553 s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
555 if (s_interrupt_flag != Qfalse) {
556 static unsigned long sLastTime = 0;
557 unsigned long currentTime;
559 currentTime = s_GetTimerCount();
560 if (currentTime != sLastTime) {
561 sLastTime = currentTime;
562 gMolbyIsCheckingInterrupt = 1;
563 flag = MyAppCallback_checkInterrupt();
564 gMolbyIsCheckingInterrupt = 0;
566 s_SetInterruptFlag(Qnil, Qfalse);
573 #pragma mark ====== Menu handling ======
577 * register_menu(title, method)
579 * Register the method (specified as a symbol) in the script menu.
580 * The method must be either an instance method of Molecule with no argument,
581 * or a class method of Molecule with one argument (the current molecule).
582 * The menu associated with the class method can be invoked even when no document
583 * is open (the argument is set to Qnil in this case). On the other hand, the
584 * menu associated with the instance method can only be invoked when at least one
585 * document is active.
588 s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
590 if (TYPE(method) == T_SYMBOL) {
591 method = rb_funcall(method, rb_intern("to_s"), 0);
593 MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
598 s_Kernel_LookupMenu(VALUE self, VALUE title)
600 int n = MyAppCallback_lookupScriptMenu(StringValuePtr(title));
605 s_Ruby_methodType_sub(VALUE data)
607 const char **p = (const char **)data;
608 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
609 ID mid = rb_intern(p[1]);
611 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
613 else if (rb_respond_to(klass, mid))
616 return INT2FIX(ival);
619 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
620 has the singleton method (class method) with the given name, 0 otherwise. */
622 Ruby_methodType(const char *className, const char *methodName)
629 retval = rb_protect(s_Ruby_methodType_sub, (VALUE)p, &status);
631 return FIX2INT(retval);
637 * execute_script_file(fname)
639 * Execute the script in the given file. If a molecule is active, then
640 * the script is evaluated as Molecule.current.instance_eval(script).
641 * Before entering the script, the current directory is set to the parent
642 * directory of the script.
645 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
648 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
658 * Get the directory suitable for storing user documents. On Windows
659 * it is the home directory + "My Documents". On other platforms
660 * it is the home directory.
663 s_Kernel_DocumentHome(VALUE self)
665 char *s = MyAppCallback_getDocumentHomeDir();
666 VALUE retval = Ruby_NewFileStringValue(s);
673 * call_subprocess(cmd, process_name)
675 * Call subprocess. A progress dialog window is displayed, with a message
676 * "Running #{process_name}...".
679 s_Kernel_CallSubProcess(VALUE self, VALUE cmd, VALUE procname)
681 int n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname));
685 #pragma mark ====== User defaults ======
689 * get_global_settings(key)
691 * Get a setting data for key from the application preferences.
694 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
696 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
698 VALUE retval = rb_eval_string(p);
706 * set_global_settings(key, value)
708 * Set a setting data for key to the application preferences.
711 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
713 VALUE sval = rb_inspect(value);
714 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
718 #pragma mark ====== Utility functions (protected funcall) ======
720 struct Ruby_funcall2_record {
728 s_Ruby_funcall2_sub(VALUE data)
730 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
731 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
735 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
737 struct Ruby_funcall2_record rec;
742 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
745 #pragma mark ====== ParameterRef Class ======
748 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
752 Data_Get_Struct(self, ParameterRef, pref);
754 *typep = pref->parType;
755 if (pref->parType == kElementParType) {
756 up = (UnionPar *)&gElementParameters[pref->idx];
758 up = ParameterRefGetPar(pref);
759 if (checkEditable && up->bond.src != 0 && up->bond.src != -1)
760 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
766 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
770 Data_Get_Struct(self, ParameterRef, pref);
771 if (pref->mol == NULL)
773 up = ParameterRefGetPar(pref);
774 if (key != s_SourceSym)
775 up->bond.src = 0; /* Becomes automatically molecule-local */
776 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
779 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
780 MolActionCallback_registerUndo(pref->mol, act);
781 MoleculeCallback_notifyModification(pref->mol, 0);
782 pref->mol->needsMDRebuild = 1;
787 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
789 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
791 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
793 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
797 s_AtomTypeIndexFromValue(VALUE val)
799 if (rb_obj_is_kind_of(val, rb_cNumeric))
802 return AtomTypeEncodeToUInt(StringValuePtr(val));
805 static const char *s_ParameterTypeNames[] = {
806 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
808 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
811 s_ParTypeFromValue(VALUE val)
815 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
816 if (s_ParameterTypeIDs[0] == 0) {
817 for (i = 0; i < n; i++)
818 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
820 valid = rb_to_id(val);
821 for (i = 0; i < n; i++) {
822 if (valid == s_ParameterTypeIDs[i]) {
824 return kElementParType;
825 else return kFirstParType + i;
828 return kInvalidParType;
835 * Get the index in the parameter list.
837 static VALUE s_ParameterRef_GetIndex(VALUE self) {
839 Data_Get_Struct(self, ParameterRef, pref);
840 return INT2NUM(pref->idx);
847 * Get the parameter type, like "bond", "angle", etc.
849 static VALUE s_ParameterRef_GetParType(VALUE self) {
851 s_UnionParFromValue(self, &tp, 0);
852 if (tp == kElementParType)
853 return rb_str_new2("element");
855 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
856 return rb_str_new2(s_ParameterTypeNames[tp]);
857 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
862 * atom_type -> String or Array of String
863 * atom_types -> String or Array of String
865 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
866 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
867 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
868 * The atom type may be "X", which is a wildcard that matches any atom type.
870 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
875 up = s_UnionParFromValue(self, &tp, 0);
876 n = ParameterGetAtomTypes(tp, up, types);
878 rb_raise(rb_eMolbyError, "invalid member atom_types");
879 for (i = 0; i < n; i++) {
880 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
881 vals[i] = INT2NUM(types[i]);
883 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
888 return rb_ary_new4(n, vals);
895 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
897 static VALUE s_ParameterRef_GetK(VALUE self) {
901 up = s_UnionParFromValue(self, &tp, 0);
904 return rb_float_new(up->bond.k * INTERNAL2KCAL);
906 return rb_float_new(up->angle.k * INTERNAL2KCAL);
907 case kDihedralParType:
908 case kImproperParType:
909 if (up->torsion.mult == 1)
910 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
911 n = up->torsion.mult;
914 for (i = 0; i < n; i++)
915 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
916 return rb_ary_new4(n, vals);
918 rb_raise(rb_eMolbyError, "invalid member k");
926 * Get the equilibrium bond length. Only available for bond parameters.
928 static VALUE s_ParameterRef_GetR0(VALUE self) {
931 up = s_UnionParFromValue(self, &tp, 0);
932 if (tp == kBondParType)
933 return rb_float_new(up->bond.r0);
934 else rb_raise(rb_eMolbyError, "invalid member r0");
941 * Get the equilibrium angle (in degree). Only available for angle parameters.
943 static VALUE s_ParameterRef_GetA0(VALUE self) {
946 up = s_UnionParFromValue(self, &tp, 0);
947 if (tp == kAngleParType)
948 return rb_float_new(up->angle.a0 * kRad2Deg);
949 else rb_raise(rb_eMolbyError, "invalid member a0");
956 * Get the multiplicity. Only available for dihedral and improper parameters.
957 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
959 static VALUE s_ParameterRef_GetMult(VALUE self) {
962 up = s_UnionParFromValue(self, &tp, 0);
963 if (tp == kDihedralParType || tp == kImproperParType)
964 return rb_float_new(up->torsion.mult);
965 else rb_raise(rb_eMolbyError, "invalid member mult");
970 * period -> Integer or Array of Integers
972 * Get the periodicity. Only available for dihedral and improper parameters.
973 * If the multiplicity is larger than 1, then an array of integers is returned.
974 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
976 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
980 up = s_UnionParFromValue(self, &tp, 0);
981 if (tp == kDihedralParType || tp == kImproperParType) {
982 if (up->torsion.mult == 1)
983 return INT2NUM(up->torsion.period[0]);
984 n = up->torsion.mult;
987 for (i = 0; i < n; i++)
988 vals[i] = INT2NUM(up->torsion.period[i]);
989 return rb_ary_new4(n, vals);
990 } else rb_raise(rb_eMolbyError, "invalid member period");
995 * phi0 -> Float or Array of Floats
997 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
998 * If the multiplicity is larger than 1, then an array of floats is returned.
999 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1001 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1005 up = s_UnionParFromValue(self, &tp, 0);
1006 if (tp == kDihedralParType || tp == kImproperParType) {
1007 if (up->torsion.mult == 1)
1008 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1009 n = up->torsion.mult;
1012 for (i = 0; i < n; i++)
1013 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1014 return rb_ary_new4(n, vals);
1015 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1022 * Get the "A" value for the van der Waals parameter.
1025 static VALUE s_ParameterRef_GetA(VALUE self) {
1028 up = s_UnionParFromValue(self, &tp, 0);
1029 if (tp == kVdwParType)
1030 return rb_float_new(up->vdw.A);
1031 else if (tp == kVdwPairParType)
1032 return rb_float_new(up->vdwp.A);
1033 else rb_raise(rb_eMolbyError, "invalid member A");
1041 * Get the "B" value for the van der Waals parameter.
1044 static VALUE s_ParameterRef_GetB(VALUE self) {
1047 up = s_UnionParFromValue(self, &tp, 0);
1048 if (tp == kVdwParType)
1049 return rb_float_new(up->vdw.B);
1050 else if (tp == kVdwPairParType)
1051 return rb_float_new(up->vdwp.B);
1052 else rb_raise(rb_eMolbyError, "invalid member B");
1060 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1062 static VALUE s_ParameterRef_GetReq(VALUE self) {
1065 /* Double a, b, r; */
1067 up = s_UnionParFromValue(self, &tp, 0);
1068 if (tp == kVdwParType) {
1072 } else if (tp == kVdwPairParType) {
1076 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1077 /* if (a == 0.0 || b == 0.0) */
1078 return rb_float_new(r);
1079 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1086 * Get the minimum energy for the van der Waals parameter.
1088 static VALUE s_ParameterRef_GetEps(VALUE self) {
1093 up = s_UnionParFromValue(self, &tp, 0);
1094 if (tp == kVdwParType) {
1098 } else if (tp == kVdwPairParType) {
1102 } else rb_raise(rb_eMolbyError, "invalid member eps");
1103 /* if (a == 0.0 || b == 0.0) */
1104 return rb_float_new(eps * INTERNAL2KCAL);
1105 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1112 * Get the "A" value for the 1-4 van der Waals parameter.
1115 static VALUE s_ParameterRef_GetA14(VALUE self) {
1118 up = s_UnionParFromValue(self, &tp, 0);
1119 if (tp == kVdwParType)
1120 return rb_float_new(up->vdw.A14);
1121 else if (tp == kVdwPairParType)
1122 return rb_float_new(up->vdwp.A14);
1123 else rb_raise(rb_eMolbyError, "invalid member A14");
1131 * Get the "B" value for the 1-4 van der Waals parameter.
1134 static VALUE s_ParameterRef_GetB14(VALUE self) {
1137 up = s_UnionParFromValue(self, &tp, 0);
1138 if (tp == kVdwParType)
1139 return rb_float_new(up->vdw.B14);
1140 else if (tp == kVdwPairParType)
1141 return rb_float_new(up->vdwp.B14);
1142 else rb_raise(rb_eMolbyError, "invalid member B14");
1150 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1152 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1155 /* Double a, b, r; */
1157 up = s_UnionParFromValue(self, &tp, 0);
1158 if (tp == kVdwParType) {
1162 } else if (tp == kVdwPairParType) {
1163 /* a = up->vdwp.A14;
1164 b = up->vdwp.B14; */
1165 r = up->vdwp.r_eq14;
1166 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1167 /* if (a == 0.0 || b == 0.0) */
1168 return rb_float_new(r);
1169 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1176 * Get the minimum energy for the 1-4 van der Waals parameter.
1178 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1183 up = s_UnionParFromValue(self, &tp, 0);
1184 if (tp == kVdwParType) {
1188 } else if (tp == kVdwPairParType) {
1189 /* a = up->vdwp.A14;
1190 b = up->vdwp.B14; */
1192 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1193 /* if (a == 0.0 || b == 0.0) */
1194 return rb_float_new(eps * INTERNAL2KCAL);
1195 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1202 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1204 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1207 up = s_UnionParFromValue(self, &tp, 0);
1208 if (tp == kVdwCutoffParType)
1209 return rb_float_new(up->vdwcutoff.cutoff);
1210 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1217 * Get the atomic radius for the atom display parameter.
1219 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1222 up = s_UnionParFromValue(self, &tp, 0);
1223 if (tp == kElementParType)
1224 return rb_float_new(up->atom.radius);
1225 else rb_raise(rb_eMolbyError, "invalid member radius");
1230 * color -> [Float, Float, Float]
1232 * Get the rgb color for the atom display parameter.
1234 static VALUE s_ParameterRef_GetColor(VALUE self) {
1237 up = s_UnionParFromValue(self, &tp, 0);
1238 if (tp == kElementParType)
1239 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1240 else rb_raise(rb_eMolbyError, "invalid member color");
1245 * atomic_number -> Integer
1247 * Get the atomic number for the vdw or atom parameter.
1249 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1252 up = s_UnionParFromValue(self, &tp, 0);
1253 if (tp == kElementParType)
1254 return INT2NUM(up->atom.number);
1255 else if (tp == kVdwParType)
1256 return INT2NUM(up->vdw.atomicNumber);
1257 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1264 * Get the name for the atom display parameter.
1266 static VALUE s_ParameterRef_GetName(VALUE self) {
1269 up = s_UnionParFromValue(self, &tp, 0);
1270 if (tp == kElementParType) {
1272 strncpy(name, up->atom.name, 4);
1274 return rb_str_new2(name);
1275 } else rb_raise(rb_eMolbyError, "invalid member name");
1282 * Get the atomic weight for the atom display parameter.
1284 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1287 up = s_UnionParFromValue(self, &tp, 0);
1288 if (tp == kElementParType)
1289 return rb_float_new(up->atom.weight);
1290 else if (tp == kVdwParType)
1291 return rb_float_new(up->vdw.weight);
1292 else rb_raise(rb_eMolbyError, "invalid member weight");
1297 * fullname -> String
1299 * Get the full name for the atom display parameter.
1301 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1304 up = s_UnionParFromValue(self, &tp, 0);
1305 if (tp == kElementParType) {
1307 strncpy(fullname, up->atom.fullname, 15);
1309 return rb_str_new2(fullname);
1310 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1317 * Get the comment for the parameter.
1319 static VALUE s_ParameterRef_GetComment(VALUE self) {
1322 up = s_UnionParFromValue(self, &tp, 0);
1326 else return rb_str_new2(ParameterGetComment(com));
1333 * Get the source string for the parameter. Returns false for undefined parameter,
1334 * and nil for "local" parameter that is specific for the molecule.
1336 static VALUE s_ParameterRef_GetSource(VALUE self) {
1339 up = s_UnionParFromValue(self, &tp, 0);
1342 return Qfalse; /* undefined */
1344 return Qnil; /* local */
1345 else return rb_str_new2(ParameterGetComment(src));
1349 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1356 if (rb_obj_is_kind_of(val, rb_cString)) {
1357 char *s = StringValuePtr(val);
1359 for (i = 0; i < n; i++) {
1362 /* Skip leading separaters */
1363 while (*s == '-' || *s == ' ' || *s == '\t')
1365 for (p = s; *p != 0; p++) {
1366 if (*p == '-' || *p == ' ' || *p == '\t')
1370 if (len >= sizeof(buf))
1371 len = sizeof(buf) - 1;
1372 strncpy(buf, s, len);
1374 /* Skip trailing blanks */
1375 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1378 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1379 if (buf[0] >= '0' && buf[0] <= '9')
1380 types[i] = atoi(buf);
1382 types[i] = AtomTypeEncodeToUInt(buf);
1383 if (p == NULL || *p == 0) {
1389 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1392 val = rb_ary_to_ary(val);
1393 if (RARRAY_LEN(val) != n)
1394 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1395 valp = RARRAY_PTR(val);
1397 for (i = 0; i < n; i++) {
1398 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1399 types[i] = NUM2INT(rb_Integer(valp[i]));
1401 VALUE sval = valp[i];
1402 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1407 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1412 up = s_UnionParFromValue(self, &tp, 1);
1413 oldval = s_ParameterRef_GetAtomTypes(self);
1414 oldsrc = up->bond.src;
1417 s_ScanAtomTypes(val, 2, types);
1418 up->bond.type1 = types[0];
1419 up->bond.type2 = types[1];
1422 s_ScanAtomTypes(val, 3, types);
1423 up->angle.type1 = types[0];
1424 up->angle.type2 = types[1];
1425 up->angle.type3 = types[2];
1427 case kDihedralParType:
1428 case kImproperParType:
1429 s_ScanAtomTypes(val, 4, types);
1430 up->torsion.type1 = types[0];
1431 up->torsion.type2 = types[1];
1432 up->torsion.type3 = types[2];
1433 up->torsion.type4 = types[3];
1436 s_ScanAtomTypes(val, 1, types);
1437 up->vdw.type1 = types[0];
1439 case kVdwPairParType:
1440 s_ScanAtomTypes(val, 2, types);
1441 up->vdwp.type1 = types[0];
1442 up->vdwp.type2 = types[1];
1444 case kVdwCutoffParType:
1445 s_ScanAtomTypes(val, 2, types);
1446 up->vdwcutoff.type1 = types[0];
1447 up->vdwcutoff.type2 = types[1];
1452 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1456 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1458 Int tp, i, n, oldsrc;
1459 VALUE *valp, oldval;
1460 up = s_UnionParFromValue(self, &tp, 1);
1461 oldval = s_ParameterRef_GetK(self);
1462 oldsrc = up->bond.src;
1465 val = rb_Float(val);
1466 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1469 val = rb_Float(val);
1470 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1472 case kDihedralParType:
1473 case kImproperParType:
1474 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1475 up->torsion.mult = 1;
1476 val = rb_Float(val);
1477 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1480 n = up->torsion.mult;
1483 val = rb_ary_to_ary(val);
1484 if (RARRAY_LEN(val) != n)
1485 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1486 valp = RARRAY_PTR(val);
1487 for (i = 0; i < n; i++) {
1488 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1492 rb_raise(rb_eMolbyError, "invalid member k");
1494 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1498 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1502 up = s_UnionParFromValue(self, &tp, 1);
1503 oldval = s_ParameterRef_GetR0(self);
1504 oldsrc = up->bond.src;
1505 if (tp == kBondParType) {
1506 val = rb_Float(val);
1507 up->bond.r0 = NUM2DBL(val);
1508 } else rb_raise(rb_eMolbyError, "invalid member r0");
1509 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1513 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1517 up = s_UnionParFromValue(self, &tp, 1);
1518 oldval = s_ParameterRef_GetA0(self);
1519 oldsrc = up->bond.src;
1520 if (tp == kAngleParType) {
1521 val = rb_Float(val);
1522 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1523 } else rb_raise(rb_eMolbyError, "invalid member a0");
1524 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1528 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1532 up = s_UnionParFromValue(self, &tp, 1);
1533 oldval = s_ParameterRef_GetMult(self);
1534 oldsrc = up->bond.src;
1535 if (tp == kDihedralParType || tp == kImproperParType) {
1537 val = rb_Integer(val);
1540 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1541 up->torsion.mult = i;
1542 } else rb_raise(rb_eMolbyError, "invalid member mult");
1543 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1547 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1549 Int tp, i, n, oldsrc;
1550 VALUE *valp, oldval;
1551 up = s_UnionParFromValue(self, &tp, 1);
1552 oldval = s_ParameterRef_GetPeriod(self);
1553 oldsrc = up->bond.src;
1554 if (tp == kDihedralParType || tp == kImproperParType) {
1555 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1556 up->torsion.mult = 1;
1557 val = rb_Integer(val);
1558 up->torsion.period[0] = NUM2INT(val);
1560 n = up->torsion.mult;
1563 val = rb_ary_to_ary(val);
1564 if (RARRAY_LEN(val) != n)
1565 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1566 valp = RARRAY_PTR(val);
1567 for (i = 0; i < n; i++) {
1568 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1571 } else rb_raise(rb_eMolbyError, "invalid member period");
1572 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1576 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1578 Int tp, i, n, oldsrc;
1579 VALUE *valp, oldval;
1580 up = s_UnionParFromValue(self, &tp, 1);
1581 oldval = s_ParameterRef_GetPhi0(self);
1582 oldsrc = up->bond.src;
1583 if (tp == kDihedralParType || tp == kImproperParType) {
1584 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1585 up->torsion.mult = 1;
1586 val = rb_Float(val);
1587 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1589 n = up->torsion.mult;
1592 val = rb_ary_to_ary(val);
1593 if (RARRAY_LEN(val) != n)
1594 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1595 valp = RARRAY_PTR(val);
1596 for (i = 0; i < n; i++)
1597 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1599 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1600 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1605 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1610 up = s_UnionParFromValue(self, &tp, 1);
1611 oldval = s_ParameterRef_GetA(self);
1612 oldsrc = up->bond.src;
1613 val = rb_Float(val);
1615 if (tp == kVdwParType)
1617 else if (tp == kVdwPairParType)
1619 else rb_raise(rb_eMolbyError, "invalid member A");
1620 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1624 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1629 up = s_UnionParFromValue(self, &tp, 1);
1630 oldval = s_ParameterRef_GetB(self);
1631 oldsrc = up->bond.src;
1632 val = rb_Float(val);
1634 if (tp == kVdwParType)
1636 else if (tp == kVdwPairParType)
1638 else rb_raise(rb_eMolbyError, "invalid member B");
1639 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1644 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1649 up = s_UnionParFromValue(self, &tp, 1);
1650 oldval = s_ParameterRef_GetReq(self);
1651 oldsrc = up->bond.src;
1652 val = rb_Float(val);
1654 if (tp == kVdwParType) {
1656 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1657 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1658 } else if (tp == kVdwPairParType) {
1660 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1661 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1662 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1663 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1667 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1672 up = s_UnionParFromValue(self, &tp, 1);
1673 oldval = s_ParameterRef_GetEps(self);
1674 oldsrc = up->bond.src;
1675 val = rb_Float(val);
1676 e = NUM2DBL(val) * KCAL2INTERNAL;
1677 if (tp == kVdwParType) {
1679 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1680 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1681 } else if (tp == kVdwPairParType) {
1683 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1684 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1685 } else rb_raise(rb_eMolbyError, "invalid member eps");
1686 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1691 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1696 up = s_UnionParFromValue(self, &tp, 1);
1697 oldval = s_ParameterRef_GetA14(self);
1698 oldsrc = up->bond.src;
1699 val = rb_Float(val);
1701 if (tp == kVdwParType)
1703 else if (tp == kVdwPairParType)
1705 else rb_raise(rb_eMolbyError, "invalid member A14");
1706 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
1710 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1715 up = s_UnionParFromValue(self, &tp, 1);
1716 oldval = s_ParameterRef_GetB14(self);
1717 oldsrc = up->bond.src;
1718 val = rb_Float(val);
1720 if (tp == kVdwParType)
1722 else if (tp == kVdwPairParType)
1724 else rb_raise(rb_eMolbyError, "invalid member B14");
1725 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
1730 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1735 up = s_UnionParFromValue(self, &tp, 1);
1736 oldval = s_ParameterRef_GetReq14(self);
1737 oldsrc = up->bond.src;
1738 val = rb_Float(val);
1740 if (tp == kVdwParType) {
1742 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1743 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1744 } else if (tp == kVdwPairParType) {
1745 up->vdwp.r_eq14 = r;
1746 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1747 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1748 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1749 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
1753 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1758 up = s_UnionParFromValue(self, &tp, 1);
1759 oldval = s_ParameterRef_GetEps14(self);
1760 oldsrc = up->bond.src;
1761 val = rb_Float(val);
1762 e = NUM2DBL(val) * KCAL2INTERNAL;
1763 if (tp == kVdwParType) {
1765 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1766 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1767 } else if (tp == kVdwPairParType) {
1769 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1770 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1771 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1772 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
1776 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
1780 oldval = s_ParameterRef_GetCutoff(self);
1781 oldsrc = up->bond.src;
1782 up = s_UnionParFromValue(self, &tp, 1);
1783 val = rb_Float(val);
1784 if (tp == kVdwCutoffParType) {
1785 up->vdwcutoff.cutoff = NUM2DBL(val);
1786 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
1787 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
1791 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
1795 up = s_UnionParFromValue(self, &tp, 1);
1796 oldval = s_ParameterRef_GetRadius(self);
1797 oldsrc = up->bond.src;
1798 val = rb_Float(val);
1799 if (tp == kElementParType) {
1800 up->atom.radius = NUM2DBL(val);
1801 } else rb_raise(rb_eMolbyError, "invalid member radius");
1802 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
1806 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
1809 VALUE *valp, oldval;
1810 up = s_UnionParFromValue(self, &tp, 1);
1811 oldval = s_ParameterRef_GetColor(self);
1812 oldsrc = up->bond.src;
1813 val = rb_ary_to_ary(val);
1814 if (RARRAY_LEN(val) != 3)
1815 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
1816 valp = RARRAY_PTR(val);
1817 if (tp == kElementParType) {
1818 up->atom.r = NUM2DBL(rb_Float(valp[0]));
1819 up->atom.g = NUM2DBL(rb_Float(valp[1]));
1820 up->atom.b = NUM2DBL(rb_Float(valp[2]));
1821 } else rb_raise(rb_eMolbyError, "invalid member color");
1822 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
1826 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
1830 up = s_UnionParFromValue(self, &tp, 1);
1831 oldval = s_ParameterRef_GetAtomicNumber(self);
1832 oldsrc = up->bond.src;
1833 val = rb_Integer(val);
1834 if (tp == kElementParType)
1835 up->atom.number = NUM2INT(val);
1836 else if (tp == kVdwParType) {
1837 up->vdw.atomicNumber = NUM2INT(val);
1838 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
1839 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1840 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
1844 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
1848 up = s_UnionParFromValue(self, &tp, 1);
1849 oldval = s_ParameterRef_GetName(self);
1850 oldsrc = up->bond.src;
1851 if (tp == kElementParType) {
1852 strncpy(up->atom.name, StringValuePtr(val), 4);
1853 } else rb_raise(rb_eMolbyError, "invalid member name");
1854 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
1858 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
1862 val = rb_Float(val);
1863 oldval = s_ParameterRef_GetWeight(self);
1864 up = s_UnionParFromValue(self, &tp, 1);
1865 oldsrc = up->bond.src;
1866 if (tp == kElementParType)
1867 up->atom.weight = NUM2DBL(val);
1868 else if (tp == kVdwParType)
1869 up->vdw.weight = NUM2DBL(val);
1870 else rb_raise(rb_eMolbyError, "invalid member weight");
1871 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
1875 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
1879 up = s_UnionParFromValue(self, &tp, 1);
1880 oldval = s_ParameterRef_GetFullName(self);
1881 oldsrc = up->bond.src;
1882 if (tp == kElementParType) {
1883 strncpy(up->atom.fullname, StringValuePtr(val), 15);
1884 up->atom.fullname[15] = 0;
1885 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1886 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
1890 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
1892 Int tp, com, oldsrc;
1894 up = s_UnionParFromValue(self, &tp, 1);
1895 oldval = s_ParameterRef_GetComment(self);
1896 oldsrc = up->bond.src;
1900 com = ParameterCommentIndex(StringValuePtr(val));
1903 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
1907 /* Only false (undefined) and nil (local) can be set */
1908 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
1912 up = s_UnionParFromValue(self, &tp, 1);
1913 if (val != Qfalse && val != Qnil)
1914 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
1915 oldval = s_ParameterRef_GetSource(self);
1916 oldsrc = up->bond.src;
1917 if (oldsrc != 0 && oldsrc != -1)
1918 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
1919 up->bond.src = (val == Qfalse ? -1 : 0);
1920 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
1924 static struct s_ParameterAttrDef {
1926 VALUE *symref; /* Address of s_IndexSymbol etc. */
1927 ID id; /* Will be set within InitMolby() */
1928 VALUE (*getter)(VALUE);
1929 VALUE (*setter)(VALUE, VALUE);
1930 } s_ParameterAttrDefTable[] = {
1931 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
1932 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
1933 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1934 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1935 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
1936 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
1937 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
1938 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
1939 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
1940 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
1941 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
1942 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
1943 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
1944 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
1945 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
1946 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
1947 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
1948 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
1949 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
1950 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
1951 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
1952 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
1953 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
1954 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
1955 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
1956 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
1957 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
1958 {NULL} /* Sentinel */
1962 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
1966 if (TYPE(key) != T_SYMBOL) {
1967 kid = rb_intern(StringValuePtr(key));
1969 } else kid = SYM2ID(key);
1970 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
1971 if (s_ParameterAttrDefTable[i].id == kid) {
1972 if (value == Qundef)
1973 return (*(s_ParameterAttrDefTable[i].getter))(self);
1975 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
1978 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
1979 return Qnil; /* not reached */
1983 s_ParameterRef_GetAttr(VALUE self, VALUE key)
1985 return s_ParameterRef_SetAttr(self, key, Qundef);
1990 * keys(idx) -> array of valid parameter attributes
1992 * Returns an array of valid parameter attributes (as Symbols).
1995 s_ParameterRef_Keys(VALUE self)
1998 Data_Get_Struct(self, ParameterRef, pref);
1999 switch (pref->parType) {
2001 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2003 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2004 case kDihedralParType:
2005 case kImproperParType:
2006 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2008 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);
2009 case kVdwPairParType:
2010 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2011 case kVdwCutoffParType:
2012 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2013 case kElementParType:
2014 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);
2016 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2018 return Qnil; /* Not reached */
2023 * to_hash(idx) -> Hash
2025 * Returns a hash containing valid parameter names and values
2028 s_ParameterRef_ToHash(VALUE self)
2030 VALUE keys = s_ParameterRef_Keys(self);
2035 retval = rb_hash_new();
2036 for (i = 0; i < RARRAY_LEN(keys); i++) {
2037 VALUE key = RARRAY_PTR(keys)[i];
2038 VALUE val = s_ParameterRef_GetAttr(self, key);
2039 rb_hash_aset(retval, key, val);
2046 * parameter.to_s(idx) -> String
2048 * Returns a string representation of the given parameter
2051 s_ParameterRef_ToString(VALUE self)
2054 char buf[1024], types[4][8];
2055 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2058 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);
2061 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);
2063 case kDihedralParType:
2064 case kImproperParType:
2065 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]));
2067 for (i = 0; i < up->torsion.mult; i++) {
2068 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);
2073 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);
2075 case kVdwPairParType:
2076 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);
2078 case kVdwCutoffParType:
2079 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);
2081 case kElementParType:
2082 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);
2085 return rb_str_new2(buf);
2088 #pragma mark ====== Parameter Class ======
2090 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2091 * is NULL, then the global parameters are looked for. */
2093 /* Rebuild the MD parameter record if necessary: may throw an exception */
2094 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2096 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2099 Data_Get_Struct(val, Molecule, mol);
2101 rb_raise(rb_eMolbyError, "the molecule is empty");
2102 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2103 /* Do self.md_arena.prepare */
2104 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2106 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2111 s_NewParameterValueFromValue(VALUE val)
2114 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2115 Data_Get_Struct(val, Molecule, mol);
2116 s_RebuildMDParameterIfNecessary(val, Qtrue);
2117 MoleculeRetain(mol);
2118 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2121 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2126 s_MoleculeFromParameterValue(VALUE val)
2129 Data_Get_Struct(val, Molecule, mol);
2134 s_ParameterFromParameterValue(VALUE val)
2137 Data_Get_Struct(val, Molecule, mol);
2140 return gBuiltinParameters;
2143 /* Forward declarations */
2144 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2145 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2148 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2150 if (rb_obj_is_kind_of(val, rb_cParameter)) {
2151 return s_MoleculeFromParameterValue(val);
2152 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2153 return s_MoleculeFromParEnumerableValue(val);
2159 * builtin -> Parameter
2161 * Returns a parameter value that points to the global (builtin) parameters.
2162 * Equivalent to Parameter::Builtin (constant).
2165 s_Parameter_Builtin(VALUE self)
2167 static ID s_builtin_id = 0;
2168 if (s_builtin_id == 0)
2169 s_builtin_id = rb_intern("Builtin");
2170 return rb_const_get(rb_cParameter, s_builtin_id);
2175 * bond(idx) -> ParameterRef
2177 * The index-th bond parameter record is returned.
2180 s_Parameter_Bond(VALUE self, VALUE ival)
2184 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2185 idx = NUM2INT(rb_Integer(ival));
2187 n = gBuiltinParameters->nbondPars;
2188 else if (mol->par != NULL)
2189 n = mol->par->nbondPars;
2191 if (idx < -n || idx >= n)
2192 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2195 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2200 * angle(idx) -> ParameterRef
2202 * The index-th angle parameter record is returned.
2205 s_Parameter_Angle(VALUE self, VALUE ival)
2209 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2210 idx = NUM2INT(rb_Integer(ival));
2212 n = gBuiltinParameters->nanglePars;
2213 else if (mol->par != NULL)
2214 n = mol->par->nanglePars;
2216 if (idx < -n || idx >= n)
2217 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2220 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2225 * dihedral(idx) -> ParameterRef
2227 * The index-th dihedral parameter record is returned.
2230 s_Parameter_Dihedral(VALUE self, VALUE ival)
2234 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2235 idx = NUM2INT(rb_Integer(ival));
2237 n = gBuiltinParameters->ndihedralPars;
2238 else if (mol->par != NULL)
2239 n = mol->par->ndihedralPars;
2241 if (idx < -n || idx >= n)
2242 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2245 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2250 * improper(idx) -> ParameterRef
2252 * The index-th improper parameter record is returned.
2255 s_Parameter_Improper(VALUE self, VALUE ival)
2259 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2260 idx = NUM2INT(rb_Integer(ival));
2262 n = gBuiltinParameters->nimproperPars;
2263 else if (mol->par != NULL)
2264 n = mol->par->nimproperPars;
2266 if (idx < -n || idx >= n)
2267 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2270 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2275 * vdw(idx) -> ParameterRef
2277 * The index-th vdw parameter record is returned.
2280 s_Parameter_Vdw(VALUE self, VALUE ival)
2284 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2285 idx = NUM2INT(rb_Integer(ival));
2287 n = gBuiltinParameters->nvdwPars;
2288 else if (mol->par != NULL)
2289 n = mol->par->nvdwPars;
2291 if (idx < -n || idx >= n)
2292 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2295 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2300 * vdw_pair(idx) -> ParameterRef
2302 * The index-th vdw pair parameter record is returned.
2305 s_Parameter_VdwPair(VALUE self, VALUE ival)
2309 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2310 idx = NUM2INT(rb_Integer(ival));
2312 n = gBuiltinParameters->nvdwpPars;
2313 else if (mol->par != NULL)
2314 n = mol->par->nvdwpPars;
2316 if (idx < -n || idx >= n)
2317 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2320 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2325 * vdw_cutoff(idx) -> ParameterRef
2327 * The index-th vdw cutoff parameter record is returned.
2330 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2334 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2335 idx = NUM2INT(rb_Integer(ival));
2337 n = gBuiltinParameters->nvdwCutoffPars;
2338 else if (mol->par != NULL)
2339 n = mol->par->nvdwCutoffPars;
2341 if (idx < -n || idx >= n)
2342 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2345 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2350 * element(idx) -> ParameterRef
2351 * element(t1) -> ParameterRef
2353 * In the first form, the index-th element parameter record is returned. In the second
2354 * form, the element parameter for t1 is looked up (the last index first). t1
2355 * is the element name string (up to 4 characters).
2356 * Unlike other Parameter methods, this is used only for the global parameter.
2359 s_Parameter_Element(VALUE self, VALUE ival)
2362 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2363 int n = gCountElementParameters;
2364 idx1 = NUM2INT(rb_Integer(ival));
2365 if (idx1 < -n || idx1 >= n)
2366 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2369 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2374 strncpy(name, StringValuePtr(ival), 4);
2376 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--) {
2377 if (strncmp(ep->name, name, 4) == 0)
2378 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2388 * Returns the number of bond parameters.
2391 s_Parameter_Nbonds(VALUE self)
2394 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2396 n = gBuiltinParameters->nbondPars;
2397 else if (mol->par != NULL)
2398 n = mol->par->nbondPars;
2405 * nangles -> Integer
2407 * Returns the number of angle parameters.
2410 s_Parameter_Nangles(VALUE self)
2413 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2415 n = gBuiltinParameters->nanglePars;
2416 else if (mol->par != NULL)
2417 n = mol->par->nanglePars;
2424 * ndihedrals -> Integer
2426 * Returns the number of dihedral parameters.
2429 s_Parameter_Ndihedrals(VALUE self)
2432 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2434 n = gBuiltinParameters->ndihedralPars;
2435 else if (mol->par != NULL)
2436 n = mol->par->ndihedralPars;
2443 * nimpropers -> Integer
2445 * Returns the number of improper parameters.
2448 s_Parameter_Nimpropers(VALUE self)
2451 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2453 n = gBuiltinParameters->nimproperPars;
2454 else if (mol->par != NULL)
2455 n = mol->par->nimproperPars;
2464 * Returns the number of vdw parameters.
2467 s_Parameter_Nvdws(VALUE self)
2470 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2472 n = gBuiltinParameters->nvdwPars;
2473 else if (mol->par != NULL)
2474 n = mol->par->nvdwPars;
2481 * nvdw_pairs -> Integer
2483 * Returns the number of vdw pair parameters.
2486 s_Parameter_NvdwPairs(VALUE self)
2489 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2491 n = gBuiltinParameters->nvdwpPars;
2492 else if (mol->par != NULL)
2493 n = mol->par->nvdwpPars;
2500 * nvdw_cutoffs -> Integer
2502 * Returns the number of vdw cutoff parameters.
2505 s_Parameter_NvdwCutoffs(VALUE self)
2508 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2510 n = gBuiltinParameters->nvdwCutoffPars;
2511 else if (mol->par != NULL)
2512 n = mol->par->nvdwCutoffPars;
2519 * nelements -> Integer
2521 * Returns the number of element parameters.
2524 s_Parameter_Nelements(VALUE self)
2526 return INT2NUM(gCountElementParameters);
2531 * bonds -> ParEnumerable
2533 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2534 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2535 * useful when all accessible parameters should be examined by use of 'each' method.
2538 s_Parameter_Bonds(VALUE self)
2540 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2541 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2546 * angles -> ParEnumerable
2548 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2549 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2550 * useful when all accessible parameters should be examined by use of 'each' method.
2553 s_Parameter_Angles(VALUE self)
2555 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2556 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2561 * dihedrals -> ParEnumerable
2563 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2564 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2565 * useful when all accessible parameters should be examined by use of 'each' method.
2568 s_Parameter_Dihedrals(VALUE self)
2570 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2571 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2576 * impropers -> ParEnumerable
2578 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2579 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2580 * useful when all accessible parameters should be examined by use of 'each' method.
2583 s_Parameter_Impropers(VALUE self)
2585 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2586 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2591 * vdws -> ParEnumerable
2593 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2594 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2595 * useful when all accessible parameters should be examined by use of 'each' method.
2598 s_Parameter_Vdws(VALUE self)
2600 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2601 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2606 * vdw_pairs -> ParEnumerable
2608 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2609 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2610 * useful when all accessible parameters should be examined by use of 'each' method.
2613 s_Parameter_VdwPairs(VALUE self)
2615 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2616 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2621 * vdw_cutoffs -> ParEnumerable
2623 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2624 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2625 * useful when all accessible parameters should be examined by use of 'each' method.
2628 s_Parameter_VdwCutoffs(VALUE self)
2630 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2631 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2636 * elements -> ParEnumerable
2638 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2639 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2640 * useful when all accessible parameters should be examined by use of 'each' method.
2643 s_Parameter_Elements(VALUE self)
2645 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2646 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2650 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2652 VALUE atval, optval;
2654 int i, n, idx, flags, is_global;
2656 rb_scan_args(argc, argv, "1*", &atval, &optval);
2658 /* Get the atom types */
2660 case kBondParType: n = 2; break;
2661 case kAngleParType: n = 3; break;
2662 case kDihedralParType: n = 4; break;
2663 case kImproperParType: n = 4; break;
2664 case kVdwParType: n = 1; break;
2665 case kVdwPairParType: n = 2; break;
2666 default: return Qnil;
2668 s_ScanAtomTypes(atval, n, t);
2670 /* Analyze options */
2672 n = RARRAY_LEN(optval);
2673 for (i = 0; i < n; i++) {
2674 VALUE oval = RARRAY_PTR(optval)[i];
2675 if (oval == ID2SYM(rb_intern("global")))
2676 flags |= kParameterLookupGlobal;
2677 else if (oval == ID2SYM(rb_intern("local")))
2678 flags |= kParameterLookupLocal;
2679 else if (oval == ID2SYM(rb_intern("missing")))
2680 flags |= kParameterLookupMissing;
2681 else if (oval == ID2SYM(rb_intern("nowildcard")))
2682 flags |= kParameterLookupNoWildcard;
2683 else if (oval == ID2SYM(rb_intern("nobasetype")))
2684 flags |= kParameterLookupNoBaseAtomType;
2685 else if (oval == ID2SYM(rb_intern("create")))
2689 flags = kParameterLookupGlobal | kParameterLookupLocal;
2694 case kBondParType: {
2697 bp = ParameterLookupBondPar(mol->par, t[0], t[1], -1, -1, flags);
2699 idx = bp - mol->par->bondPars;
2703 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2705 idx = bp - gBuiltinParameters->bondPars;
2710 case kAngleParType: {
2713 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], -1, -1, -1, flags);
2715 idx = ap - mol->par->anglePars;
2719 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2721 idx = ap - gBuiltinParameters->anglePars;
2726 case kDihedralParType: {
2729 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2731 idx = tp - mol->par->dihedralPars;
2735 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2737 idx = tp - gBuiltinParameters->dihedralPars;
2742 case kImproperParType: {
2745 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2747 idx = tp - mol->par->improperPars;
2751 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2753 idx = tp - gBuiltinParameters->improperPars;
2761 vp = ParameterLookupVdwPar(mol->par, t[0], flags);
2763 idx = vp - mol->par->vdwPars;
2767 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], flags);
2769 idx = vp - gBuiltinParameters->vdwPars;
2774 case kVdwPairParType: {
2777 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], flags);
2779 idx = vp - mol->par->vdwpPars;
2783 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], flags);
2785 idx = vp - gBuiltinParameters->vdwpPars;
2794 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
2797 /* Insert a new parameter record */
2799 Int count = ParameterGetCountForType(mol->par, parType);
2800 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
2801 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
2802 IntGroupRelease(ig);
2805 /* Set atom types */
2806 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
2811 up->bond.type1 = t[0];
2812 up->bond.type2 = t[1];
2815 up->angle.type1 = t[0];
2816 up->angle.type2 = t[1];
2817 up->angle.type3 = t[2];
2819 case kDihedralParType:
2820 case kImproperParType:
2821 up->torsion.type1 = t[0];
2822 up->torsion.type2 = t[1];
2823 up->torsion.type3 = t[2];
2824 up->torsion.type4 = t[3];
2827 up->vdw.type1 = t[0];
2829 case kVdwPairParType:
2830 up->vdwp.type1 = t[0];
2831 up->vdwp.type2 = t[1];
2838 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
2843 * lookup(par_type, atom_types, options, ...) -> ParameterRef
2844 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
2846 * Find the parameter record that matches the given atom types. The atom types are given
2847 * either as an array of string, or a single string delimited by whitespaces or hyphens.
2848 * Options are given as symbols. Valid values are :global (look for global parameters), :local
2849 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
2850 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
2853 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
2856 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2858 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
2859 parType = s_ParTypeFromValue(argv[0]);
2860 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
2863 #pragma mark ====== ParEnumerable Class ======
2865 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
2866 and the parameter type. If the Molecule is NULL, then it refers to the
2867 global (built-in) parameters. Note that, even when the Molecule is not NULL,
2868 the global parameters are always accessible. */
2870 typedef struct ParEnumerable {
2872 Int parType; /* Same as parType in ParameterRef */
2875 static ParEnumerable *
2876 s_ParEnumerableNew(Molecule *mol, Int parType)
2878 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
2882 MoleculeRetain(mol);
2883 pen->parType = parType;
2889 s_ParEnumerableRelease(ParEnumerable *pen)
2892 if (pen->mol != NULL)
2893 MoleculeRelease(pen->mol);
2899 s_MoleculeFromParEnumerableValue(VALUE val)
2902 Data_Get_Struct(val, ParEnumerable, pen);
2907 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
2909 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
2911 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
2912 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
2917 * par_type -> String
2919 * Get the parameter type, like "bond", "angle", etc.
2922 s_ParEnumerable_ParType(VALUE self) {
2925 Data_Get_Struct(self, ParEnumerable, pen);
2927 if (tp == kElementParType)
2928 return rb_str_new2("element");
2929 tp -= kFirstParType;
2930 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
2931 return rb_str_new2(s_ParameterTypeNames[tp]);
2932 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
2937 * self[idx] -> ParameterRef
2939 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
2940 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
2941 * parent Parameter object of self.
2943 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
2944 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
2947 s_ParEnumerable_Aref(VALUE self, VALUE ival)
2950 Data_Get_Struct(self, ParEnumerable, pen);
2951 switch (pen->parType) {
2952 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
2953 case kBondParType: return s_Parameter_Bond(self, ival);
2954 case kAngleParType: return s_Parameter_Angle(self, ival);
2955 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
2956 case kImproperParType: return s_Parameter_Improper(self, ival);
2957 case kVdwParType: return s_Parameter_Vdw(self, ival);
2958 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
2959 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
2960 case kElementParType: return s_Parameter_Element(self, ival);
2962 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
2964 return Qnil; /* Not reached */
2971 * Returns the number of parameters included in this enumerable.
2974 s_ParEnumerable_Length(VALUE self)
2977 Data_Get_Struct(self, ParEnumerable, pen);
2978 switch (pen->parType) {
2979 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
2980 case kBondParType: return s_Parameter_Nbonds(self);
2981 case kAngleParType: return s_Parameter_Nangles(self);
2982 case kDihedralParType: return s_Parameter_Ndihedrals(self);
2983 case kImproperParType: return s_Parameter_Nimpropers(self);
2984 case kVdwParType: return s_Parameter_Nvdws(self);
2985 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
2986 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
2987 case kElementParType: return s_Parameter_Nelements(self);
2989 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
2991 return Qnil; /* Not reached */
2998 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3001 s_ParEnumerable_Each(VALUE self)
3007 Data_Get_Struct(self, ParEnumerable, pen);
3008 if (pen->parType == kElementParType)
3009 n = gCountElementParameters;
3011 switch (pen->parType) {
3012 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3013 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3014 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3015 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3016 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3017 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3018 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3020 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3022 if (pen->mol == NULL)
3023 n = *((Int *)((char *)gBuiltinParameters + ofs));
3024 else if (pen->mol->par != NULL)
3025 n = *((Int *)((char *)(pen->mol->par) + ofs));
3028 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3029 Data_Get_Struct(aval, ParameterRef, pref);
3030 for (i = 0; i < n; i++) {
3039 * reverse_each {|pref| ...}
3041 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3044 s_ParEnumerable_ReverseEach(VALUE self)
3050 Data_Get_Struct(self, ParEnumerable, pen);
3051 if (pen->parType == kElementParType)
3052 n = gCountElementParameters;
3054 switch (pen->parType) {
3055 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3056 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3057 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3058 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3059 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3060 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3061 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3063 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3065 if (pen->mol == NULL)
3066 n = *((Int *)((char *)gBuiltinParameters + ofs));
3067 else if (pen->mol->par != NULL)
3068 n = *((Int *)((char *)(pen->mol->par) + ofs));
3071 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3072 Data_Get_Struct(aval, ParameterRef, pref);
3073 for (i = n - 1; i >= 0; i--) {
3082 * insert(idx = nil, pref = nil) -> ParameterRef
3084 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3085 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3086 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3087 * parameter is left undefined.
3088 * Throws an exception if ParEnumerable points to the global parameter.
3091 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3099 Data_Get_Struct(self, ParEnumerable, pen);
3100 if (pen->mol == NULL)
3101 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3102 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3103 rb_scan_args(argc, argv, "02", &ival, &pval);
3105 i = NUM2INT(rb_Integer(ival));
3107 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3112 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3113 if (up == NULL || type != pen->parType)
3114 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3115 ParameterCopyOneWithType(&u, up, pen->parType);
3118 memset(&u, 0, sizeof(u));
3121 ig = IntGroupNewWithPoints(n, 1, -1);
3122 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3124 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3125 MolActionCallback_registerUndo(pen->mol, act);
3126 MolActionRelease(act);
3128 IntGroupRelease(ig);
3129 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3137 * Delete the parameter(s) specified by the argument.
3140 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3145 Data_Get_Struct(self, ParEnumerable, pen);
3146 if (pen->mol == NULL)
3147 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3148 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3149 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3150 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3153 ig = IntGroupFromValue(ival);
3154 if ((i = IntGroupGetCount(ig)) == 0) {
3155 IntGroupRelease(ig);
3159 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3160 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3163 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3164 IntGroupRelease(ig);
3170 * lookup(atom_types, options, ...) -> ParameterRef
3171 * lookup(atom_type_string, options, ...) -> ParameterRef
3173 * Find the parameter record that matches the given atom types. The arguments are
3174 * the same as Parameter#lookup, except for the parameter type which is implicitly
3178 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3181 Data_Get_Struct(self, ParEnumerable, pen);
3182 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3185 #pragma mark ====== AtomRef Class ======
3187 /* Forward declaration for register undo */
3188 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3190 /* Ruby string "set_atom_attr" */
3191 static VALUE s_SetAtomAttrString;
3194 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3198 Data_Get_Struct(self, AtomRef, aref);
3199 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3200 if (idx < 0 || idx >= aref->mol->natoms)
3201 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3203 *app = aref->mol->atoms + idx;
3210 s_AtomFromValue(VALUE self)
3213 s_AtomIndexFromValue(self, &ap, NULL);
3218 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3221 s_AtomIndexFromValue(self, &ap, mpp);
3226 s_NotifyModificationForAtomRef(VALUE self)
3229 Data_Get_Struct(self, AtomRef, aref);
3230 MoleculeIncrementModifyCount(aref->mol);
3234 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3237 Data_Get_Struct(self, AtomRef, aref);
3238 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3241 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3242 MolActionCallback_registerUndo(aref->mol, act);
3243 MoleculeCallback_notifyModification(aref->mol, 0);
3244 /* Request MD rebuilt if necessary */
3245 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3246 aref->mol->needsMDRebuild = 1;
3251 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3254 aref = AtomRefNew(mol, idx);
3255 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3259 s_AtomRef_GetMolecule(VALUE self)
3262 s_AtomIndexFromValue(self, NULL, &mpp);
3263 return ValueFromMolecule(mpp);
3266 static VALUE s_AtomRef_GetIndex(VALUE self) {
3267 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3270 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3271 return INT2NUM(s_AtomFromValue(self)->segSeq);
3274 static VALUE s_AtomRef_GetSegName(VALUE self) {
3275 char *p = s_AtomFromValue(self)->segName;
3276 return rb_str_new(p, strlen_limit(p, 4));
3279 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3280 return INT2NUM(s_AtomFromValue(self)->resSeq);
3283 static VALUE s_AtomRef_GetResName(VALUE self) {
3284 char *p = s_AtomFromValue(self)->resName;
3285 return rb_str_new(p, strlen_limit(p, 4));
3288 static VALUE s_AtomRef_GetName(VALUE self) {
3289 char *p = s_AtomFromValue(self)->aname;
3290 return rb_str_new(p, strlen_limit(p, 4));
3293 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3294 int type = s_AtomFromValue(self)->type;
3295 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3296 return rb_str_new(p, strlen_limit(p, 6));
3299 static VALUE s_AtomRef_GetCharge(VALUE self) {
3300 return rb_float_new(s_AtomFromValue(self)->charge);
3303 static VALUE s_AtomRef_GetWeight(VALUE self) {
3304 return rb_float_new(s_AtomFromValue(self)->weight);
3307 static VALUE s_AtomRef_GetElement(VALUE self) {
3308 char *p = s_AtomFromValue(self)->element;
3309 return rb_str_new(p, strlen_limit(p, 4));
3312 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3313 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3316 static VALUE s_AtomRef_GetConnects(VALUE self) {
3319 Atom *ap = s_AtomFromValue(self);
3320 retval = rb_ary_new();
3321 cp = AtomConnectData(&ap->connect);
3322 for (i = 0; i < ap->connect.count; i++)
3323 rb_ary_push(retval, INT2NUM(cp[i]));
3327 static VALUE s_AtomRef_GetR(VALUE self) {
3328 return ValueFromVector(&(s_AtomFromValue(self)->r));
3331 static VALUE s_AtomRef_GetX(VALUE self) {
3332 return rb_float_new(s_AtomFromValue(self)->r.x);
3335 static VALUE s_AtomRef_GetY(VALUE self) {
3336 return rb_float_new(s_AtomFromValue(self)->r.y);
3339 static VALUE s_AtomRef_GetZ(VALUE self) {
3340 return rb_float_new(s_AtomFromValue(self)->r.z);
3343 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3347 s_AtomIndexFromValue(self, &ap, &mp);
3349 if (mp->cell != NULL)
3350 TransformVec(&r1, mp->cell->rtr, &r1);
3354 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3355 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3356 return ValueFromVector(&r1);
3359 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3360 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3363 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3364 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3367 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3368 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3371 static VALUE s_AtomRef_GetSigma(VALUE self) {
3372 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3375 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3376 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3379 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3380 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3383 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3384 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3387 static VALUE s_AtomRef_GetV(VALUE self) {
3388 return ValueFromVector(&(s_AtomFromValue(self)->v));
3391 static VALUE s_AtomRef_GetF(VALUE self) {
3392 return ValueFromVector(&(s_AtomFromValue(self)->f));
3395 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3396 return rb_float_new(s_AtomFromValue(self)->occupancy);
3399 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3400 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3403 static VALUE s_AtomRef_GetAniso(VALUE self) {
3406 Atom *ap = s_AtomFromValue(self);
3407 if (ap->aniso == NULL)
3409 retval = rb_ary_new();
3410 for (i = 0; i < 6; i++)
3411 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3412 if (ap->aniso->has_bsig) {
3413 rb_ary_push(retval, INT2NUM(0));
3414 for (i = 0; i < 6; i++)
3415 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3420 static VALUE s_AtomRef_GetSymop(VALUE self) {
3422 Atom *ap = s_AtomFromValue(self);
3423 if (!ap->symop.alive)
3425 retval = rb_ary_new();
3426 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3427 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3428 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3429 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3430 rb_ary_push(retval, INT2NUM(ap->symbase));
3434 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3435 return INT2NUM(s_AtomFromValue(self)->intCharge);
3438 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3439 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3442 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3443 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3446 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3450 MDExclusion *exinfo;
3453 idx = s_AtomIndexFromValue(self, &ap, &mol);
3454 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3455 VALUE mval = ValueFromMolecule(mol);
3456 s_RebuildMDParameterIfNecessary(mval, Qnil);
3458 if (mol->arena->exinfo == NULL)
3460 exinfo = mol->arena->exinfo + idx;
3461 exlist = mol->arena->exlist;
3462 retval = rb_ary_new();
3463 aval = rb_ary_new();
3464 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3465 rb_ary_push(aval, INT2NUM(exlist[i]));
3466 rb_ary_push(retval, aval);
3467 aval = rb_ary_new();
3468 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3469 rb_ary_push(aval, INT2NUM(exlist[i]));
3470 rb_ary_push(retval, aval);
3471 aval = rb_ary_new();
3472 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3473 rb_ary_push(aval, INT2NUM(exlist[i]));
3474 rb_ary_push(retval, aval);
3478 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3479 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3482 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3483 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3486 static VALUE s_AtomRef_GetHidden(VALUE self) {
3487 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3490 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3491 rb_raise(rb_eMolbyError, "index cannot be directly set");
3495 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3496 VALUE oval = s_AtomRef_GetSegSeq(self);
3497 val = rb_Integer(val);
3498 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3499 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3503 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3504 char *p = StringValuePtr(val);
3505 VALUE oval = s_AtomRef_GetSegName(self);
3506 strncpy(s_AtomFromValue(self)->segName, p, 4);
3507 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3511 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3512 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3513 return val; /* Not reached */
3516 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3517 char *p = StringValuePtr(val);
3518 VALUE oval = s_AtomRef_GetName(self);
3519 strncpy(s_AtomFromValue(self)->aname, p, 4);
3520 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3524 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3526 char *p = StringValuePtr(val);
3527 VALUE oval = s_AtomRef_GetAtomType(self);
3528 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3529 if (type != 0 && type < kAtomTypeMinimum)
3530 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3531 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3532 mp->needsMDRebuild = 1;
3533 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3537 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3539 VALUE oval = s_AtomRef_GetCharge(self);
3540 val = rb_Float(val);
3541 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3542 mp->needsMDRebuild = 1;
3543 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3547 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3549 VALUE oval = s_AtomRef_GetWeight(self);
3550 val = rb_Float(val);
3551 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3552 mp->needsMDRebuild = 1;
3553 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3557 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3560 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3561 char *p = StringValuePtr(val);
3562 VALUE oval = s_AtomRef_GetElement(self);
3563 ap->atomicNumber = ElementToInt(p);
3564 ElementToString(ap->atomicNumber, ap->element);
3565 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3567 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3568 mp->needsMDRebuild = 1;
3572 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3575 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3576 VALUE oval = s_AtomRef_GetAtomicNumber(self);
3577 val = rb_Integer(val);
3578 ap->atomicNumber = NUM2INT(val);
3579 ElementToString(ap->atomicNumber, ap->element);
3580 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3582 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3583 mp->needsMDRebuild = 1;
3587 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3588 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3589 return val; /* Not reached */
3592 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3595 VALUE oval = s_AtomRef_GetR(self);
3596 VectorFromValue(val, &v);
3597 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3598 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3599 mp->needsMDCopyCoordinates = 1;
3603 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3606 VALUE oval = s_AtomRef_GetX(self);
3607 val = rb_Float(val);
3609 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3610 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3611 mp->needsMDCopyCoordinates = 1;
3615 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3618 VALUE oval = s_AtomRef_GetY(self);
3619 val = rb_Float(val);
3621 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3622 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3623 mp->needsMDCopyCoordinates = 1;
3627 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3630 VALUE oval = s_AtomRef_GetZ(self);
3631 val = rb_Float(val);
3633 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3634 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3635 mp->needsMDCopyCoordinates = 1;
3639 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3643 s_AtomIndexFromValue(self, &ap, &mp);
3645 VectorFromValue(val, &v);
3646 if (mp->cell != NULL)
3647 TransformVec(&v, mp->cell->tr, &v);
3649 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3650 mp->needsMDCopyCoordinates = 1;
3654 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3659 s_AtomIndexFromValue(self, &ap, &mp);
3661 val = rb_Float(val);
3663 if (mp->cell != NULL) {
3664 TransformVec(&v, mp->cell->rtr, &v);
3666 TransformVec(&v, mp->cell->tr, &v);
3669 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3670 mp->needsMDCopyCoordinates = 1;
3674 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
3679 s_AtomIndexFromValue(self, &ap, &mp);
3681 val = rb_Float(val);
3683 if (mp->cell != NULL) {
3684 TransformVec(&v, mp->cell->rtr, &v);
3686 TransformVec(&v, mp->cell->tr, &v);
3689 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3690 mp->needsMDCopyCoordinates = 1;
3694 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
3699 s_AtomIndexFromValue(self, &ap, &mp);
3701 val = rb_Float(val);
3703 if (mp->cell != NULL) {
3704 TransformVec(&v, mp->cell->rtr, &v);
3706 TransformVec(&v, mp->cell->tr, &v);
3709 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3710 mp->needsMDCopyCoordinates = 1;
3714 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
3717 VALUE oval = s_AtomRef_GetSigma(self);
3718 VectorFromValue(val, &v);
3719 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
3720 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
3721 mp->needsMDCopyCoordinates = 1;
3725 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
3728 VALUE oval = s_AtomRef_GetSigmaX(self);
3729 val = rb_Float(val);
3731 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
3732 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
3733 mp->needsMDCopyCoordinates = 1;
3737 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
3740 VALUE oval = s_AtomRef_GetSigmaY(self);
3741 val = rb_Float(val);
3743 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
3744 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
3745 mp->needsMDCopyCoordinates = 1;
3749 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
3752 VALUE oval = s_AtomRef_GetSigmaZ(self);
3753 val = rb_Float(val);
3755 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
3756 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
3757 mp->needsMDCopyCoordinates = 1;
3761 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
3765 VALUE oval = s_AtomRef_GetV(self);
3766 VectorFromValue(val, &v);
3767 s_AtomIndexFromValue(self, &ap, &mp);
3769 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
3770 mp->needsMDCopyCoordinates = 1;
3774 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
3777 VALUE oval = s_AtomRef_GetF(self);
3778 VectorFromValue(val, &v);
3779 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
3780 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
3781 mp->needsMDCopyCoordinates = 1;
3785 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
3786 VALUE oval = s_AtomRef_GetOccupancy(self);
3788 val = rb_Float(val);
3789 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
3790 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
3791 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
3795 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
3796 VALUE oval = s_AtomRef_GetTempFactor(self);
3797 val = rb_Float(val);
3798 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
3799 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
3803 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
3808 VALUE oval = s_AtomRef_GetAniso(self);
3809 Data_Get_Struct(self, AtomRef, aref);
3810 val = rb_funcall(val, rb_intern("to_a"), 0);
3811 n = RARRAY_LEN(val);
3812 valp = RARRAY_PTR(val);
3813 for (i = 0; i < 6; i++) {
3815 f[i] = NUM2DBL(rb_Float(valp[i]));
3819 type = NUM2INT(rb_Integer(valp[6]));
3822 for (i = 0; i < 6; i++)
3823 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
3825 for (i = 0; i < 6; i++)
3828 i = s_AtomIndexFromValue(self, NULL, NULL);
3829 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
3830 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
3834 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
3840 VALUE oval = s_AtomRef_GetSymop(self);
3841 i = s_AtomIndexFromValue(self, &ap, &mol);
3843 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
3845 val = rb_funcall(val, rb_intern("to_a"), 0);
3846 n = RARRAY_LEN(val);
3847 valp = RARRAY_PTR(val);
3848 for (i = 0; i < 5; i++) {
3850 if (valp[i] == Qnil)
3853 ival[i] = NUM2INT(rb_Integer(valp[i]));
3854 } else ival[i] = -100000;
3857 if (ival[0] != -100000 && (ival[0] < 0 || ival[0] >= mol->nsyms))
3858 rb_raise(rb_eMolbyError, "index of symmetry (%d) is out of range (should be 0..%d)", ival[0], mol->nsyms - 1);
3859 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
3860 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
3861 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
3862 return val; /* No need to change */
3863 if (ival[0] != -100000)
3864 ap->symop.sym = ival[0];
3865 if (ival[1] != -100000)
3866 ap->symop.dx = ival[1];
3867 if (ival[2] != -100000)
3868 ap->symop.dy = ival[2];
3869 if (ival[3] != -100000)
3870 ap->symop.dz = ival[3];
3871 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
3872 if (ival[4] != -100000)
3873 ap->symbase = ival[4];
3874 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
3875 /* The anisotropic parameters should be recalculated */
3876 VALUE oaval = s_AtomRef_GetAniso(self);
3877 MoleculeSetAnisoBySymop(mol, i);
3878 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
3880 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
3884 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
3885 VALUE oval = s_AtomRef_GetIntCharge(self);
3886 val = rb_Integer(val);
3887 s_AtomFromValue(self)->intCharge = NUM2INT(val);
3888 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
3892 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
3894 VALUE oval = s_AtomRef_GetFixForce(self);
3895 val = rb_Float(val);
3896 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
3897 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
3898 mp->needsMDRebuild = 1;
3902 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
3905 VALUE oval = s_AtomRef_GetFixPos(self);
3906 VectorFromValue(val, &v);
3907 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
3908 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
3909 mp->needsMDRebuild = 1;
3913 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
3914 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
3915 return val; /* Not reached */
3918 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
3919 VALUE oval = s_AtomRef_GetIntCharge(self);
3920 val = rb_Integer(val);
3921 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
3922 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
3926 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
3927 VALUE oval = s_AtomRef_GetIntCharge(self);
3928 val = rb_Integer(val);
3929 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
3930 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
3934 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
3935 Atom *ap = s_AtomFromValue(self);
3936 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3938 ap->exflags |= kAtomHiddenFlag;
3940 ap->exflags &= ~kAtomHiddenFlag;
3942 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
3946 static struct s_AtomAttrDef {
3948 VALUE *symref; /* Address of s_IndexSymbol etc. */
3949 ID id; /* Will be set within InitMolby() */
3950 VALUE (*getter)(VALUE);
3951 VALUE (*setter)(VALUE, VALUE);
3952 } s_AtomAttrDefTable[] = {
3953 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
3954 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
3955 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
3956 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
3957 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
3958 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
3959 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
3960 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
3961 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
3962 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
3963 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
3964 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
3965 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
3966 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
3967 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
3968 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
3969 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
3970 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
3971 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
3972 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
3973 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
3974 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
3975 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
3976 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
3977 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
3978 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
3979 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
3980 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
3981 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
3982 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
3983 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
3984 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
3985 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
3986 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
3987 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
3988 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
3989 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
3990 {NULL} /* Sentinel */
3994 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
3998 if (TYPE(key) != T_SYMBOL) {
3999 kid = rb_intern(StringValuePtr(key));
4001 } else kid = SYM2ID(key);
4002 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4003 if (s_AtomAttrDefTable[i].id == kid) {
4004 if (value == Qundef)
4005 return (*(s_AtomAttrDefTable[i].getter))(self);
4007 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4010 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4011 return Qnil; /* not reached */
4015 s_AtomRef_GetAttr(VALUE self, VALUE key)
4017 return s_AtomRef_SetAttr(self, key, Qundef);
4020 #pragma mark ====== MolEnumerable Class ======
4022 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4026 * self[idx] -> AtomRef or Array of Integers
4028 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4029 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4030 * value is a String. Otherwise, the return value is an Array of Integers.
4033 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4035 MolEnumerable *mseq;
4038 Data_Get_Struct(self, MolEnumerable, mseq);
4040 if (mseq->kind == kAtomKind) {
4041 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4043 idx1 = NUM2INT(arg1);
4044 switch (mseq->kind) {
4046 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4047 if (idx2 < 0 || idx2 >= mol->nbonds)
4048 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4049 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4052 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4053 if (idx2 < 0 || idx2 >= mol->nangles)
4054 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4055 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4057 case kDihedralKind: {
4058 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4059 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4060 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4061 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]));
4063 case kImproperKind: {
4064 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4065 if (idx2 < 0 || idx2 >= mol->nimpropers)
4066 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4067 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]));
4069 case kResidueKind: {
4071 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4072 if (idx2 < 0 || idx2 >= mol->nresidues)
4073 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4074 p = mol->residues[idx2];
4075 return rb_str_new(p, strlen_limit(p, 4));
4085 * Returns the number of objects included in this enumerable.
4088 s_MolEnumerable_Length(VALUE self)
4090 MolEnumerable *mseq;
4091 Data_Get_Struct(self, MolEnumerable, mseq);
4092 switch (mseq->kind) {
4094 return INT2NUM(mseq->mol->natoms);
4096 return INT2NUM(mseq->mol->nbonds);
4098 return INT2NUM(mseq->mol->nangles);
4100 return INT2NUM(mseq->mol->ndihedrals);
4102 return INT2NUM(mseq->mol->nimpropers);
4104 return INT2NUM(mseq->mol->nresidues);
4113 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4114 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4115 * For the atoms, a same AtomRef object is passed (with different internal information)
4116 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4117 * for each iteration.
4120 s_MolEnumerable_Each(VALUE self)
4122 MolEnumerable *mseq;
4124 int len = NUM2INT(s_MolEnumerable_Length(self));
4125 Data_Get_Struct(self, MolEnumerable, mseq);
4126 if (mseq->kind == kAtomKind) {
4127 /* The same AtomRef object will be used during the loop */
4128 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4129 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4130 for (i = 0; i < len; i++) {
4135 /* A new ruby object will be created at each iteration (not very efficient) */
4136 for (i = 0; i < len; i++) {
4137 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4143 #pragma mark ====== Molecule Class ======
4146 MoleculeFromValue(VALUE val)
4149 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4150 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4151 Data_Get_Struct(val, Molecule, mol);
4155 static VALUE sMoleculeRetainArray = Qnil;
4157 /* The function is called from MoleculeRelease() */
4158 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4159 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4160 /* object is always returned for the same Molecule. */
4161 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4162 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4163 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4164 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4165 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4167 /* Register/unregister the exmolobj Ruby object */
4169 MoleculeReleaseExternalObj(Molecule *mol)
4171 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4172 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4173 mol->exmolobjProtected = 0;
4178 MoleculeRetainExternalObj(Molecule *mol)
4180 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4181 if (sMoleculeRetainArray == Qnil) {
4182 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4183 sMoleculeRetainArray = rb_ary_new();
4186 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4187 mol->exmolobjProtected = 1;
4191 /* Release hook function for Ruby */
4193 MoleculeReleaseHook(Molecule *mol)
4195 if (mol->exmolobj != NULL) {
4196 /* No need to remove from sMoleculeRetainArray */
4197 mol->exmolobj = NULL;
4198 mol->exmolobjProtected = 0;
4200 MoleculeRelease(mol);
4204 ValueFromMolecule(Molecule *mol)
4208 if (mol->exmolobj != NULL)
4209 return (VALUE)mol->exmolobj;
4210 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4211 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4212 return (VALUE)mol->exmolobj;
4217 s_Molecule_Alloc(VALUE klass)
4220 Molecule *mol = MoleculeNew();
4221 val = ValueFromMolecule(mol);
4222 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4227 s_Molecule_AtomOrPiAtomIndexFromValue(Molecule *mol, VALUE val)
4231 if (FIXNUM_P(val)) {
4233 if (n >= 0 && n < mol->natoms)
4235 else if (n < 0 && -n - 1 < mol->npiatoms)
4236 return ATOMS_MAX_NUMBER + (-n - 1); /* PiAtom */
4237 n = -1; /* No such atom */
4239 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4241 if (n >= 0 && n < mol->natoms)
4243 p = StringValuePtr(val);
4244 for (i = 0; i < mol->npiatoms; i++) {
4245 if (strcmp(p, mol->piatoms->aname) == 0)
4246 return ATOMS_MAX_NUMBER + i; /* PiAtom; should be looked up after normal atoms */
4249 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4251 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4253 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4254 return 0; /* Not reached */
4258 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4260 int n = s_Molecule_AtomOrPiAtomIndexFromValue(mol, val);
4261 if (n >= ATOMS_MAX_NUMBER) {
4262 char *p = StringValuePtr(val);
4263 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4269 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4272 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4273 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4274 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4275 Data_Get_Struct(val, IntGroup, ig);
4284 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4285 * created object does not affect the old object in any sense.
4288 s_Molecule_InitCopy(VALUE self, VALUE arg)
4290 Molecule *mp1, *mp2;
4291 Data_Get_Struct(self, Molecule, mp1);
4292 mp2 = MoleculeFromValue(arg);
4293 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4294 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4300 * loadmbsf(file) -> bool
4302 * Read a structure from a mbsf file.
4303 * Return true if successful.
4306 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4313 Data_Get_Struct(self, Molecule, mol);
4314 rb_scan_args(argc, argv, "1", &fname);
4315 fstr = FileStringValuePtr(fname);
4316 retval = MoleculeLoadMbsfFile(mol, fstr, errbuf, sizeof errbuf);
4318 /* if (retval == -1)
4320 rb_raise(rb_eMolbyError, errbuf);
4327 * loadpsf(file, pdbfile = nil) -> bool
4329 * Read a structure from a psf file. molecule must be empty. The psf may be
4330 * an "extended" version, which also contains coordinates. If pdbfile
4331 * is given, then atomic coordinates are read from that file.
4332 * Return true if successful.
4335 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4337 VALUE fname, pdbname;
4338 char *fstr, *pdbstr;
4342 Data_Get_Struct(self, Molecule, mol);
4343 if (mol->natoms > 0)
4344 return Qnil; /* Must be a new molecule */
4345 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4346 fstr = FileStringValuePtr(fname);
4347 retval = MoleculeLoadPsfFile(mol, fstr, errbuf, sizeof errbuf);
4349 /* if (retval == -1)
4351 rb_raise(rb_eMolbyError, errbuf);
4354 if (!NIL_P(pdbname)) {
4355 pdbstr = strdup(FileStringValuePtr(pdbname));
4357 2008.7.19. The pdbfile should be explicitly given
4359 int len = strlen(fstr);
4360 if (len > 4 && strcmp(fstr + len - 4, ".psf") == 0) {
4361 pdbstr = ALLOC_N(char, len + 1);
4362 strcpy(pdbstr, fstr);
4363 strcpy(pdbstr + len - 4, ".pdb");
4366 if (pdbstr != NULL) {
4368 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, errbuf, sizeof errbuf);
4370 /* if (retval == -1 && NIL_P(pdbname))
4375 rb_raise(rb_eMolbyError, errbuf);
4382 * loadpdb(file) -> bool
4384 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4385 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4386 * Return true if successful.
4389 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4396 Data_Get_Struct(self, Molecule, mol);
4397 rb_scan_args(argc, argv, "1", &fname);
4398 fstr = FileStringValuePtr(fname);
4399 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, errbuf, sizeof errbuf);
4401 /* if (retval == -1)
4403 rb_raise(rb_eMolbyError, errbuf);
4410 * loaddcd(file) -> bool
4412 * Read coordinates from a dcd file. The molecule should not empty.
4413 * Return true if successful.
4416 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4423 Data_Get_Struct(self, Molecule, mol);
4424 rb_scan_args(argc, argv, "1", &fname);
4425 fstr = FileStringValuePtr(fname);
4426 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, errbuf, sizeof errbuf);
4428 /* if (retval == -1)
4430 rb_raise(rb_eMolbyError, errbuf);
4437 * loadtep(file) -> bool
4439 * Read coordinates from an ortep .tep file.
4440 * Return true if successful.
4443 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4450 Data_Get_Struct(self, Molecule, mol);
4451 rb_scan_args(argc, argv, "1", &fname);
4452 fstr = FileStringValuePtr(fname);
4453 retval = MoleculeLoadTepFile(mol, fstr, errbuf, sizeof errbuf);
4455 /* if (retval == -1)
4457 rb_raise(rb_eMolbyError, errbuf);
4464 * loadres(file) -> bool
4466 * Read coordinates from a shelx .res file.
4467 * Return true if successful.
4470 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4477 Data_Get_Struct(self, Molecule, mol);
4478 rb_scan_args(argc, argv, "1", &fname);
4479 fstr = FileStringValuePtr(fname);
4480 retval = MoleculeLoadShelxFile(mol, fstr, errbuf, sizeof errbuf);
4482 /* if (retval == -1)
4484 rb_raise(rb_eMolbyError, errbuf);
4491 * loadfchk(file) -> bool
4493 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
4494 * Return true if successful.
4497 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4504 Data_Get_Struct(self, Molecule, mol);
4505 rb_scan_args(argc, argv, "1", &fname);
4506 fstr = FileStringValuePtr(fname);
4507 retval = MoleculeLoadGaussianFchkFile(mol, fstr, errbuf, sizeof errbuf);
4509 /* if (retval == -1)
4511 rb_raise(rb_eMolbyError, errbuf);
4518 * loaddat(file) -> bool
4520 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
4521 * Return true if successful.
4524 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4531 Data_Get_Struct(self, Molecule, mol);
4532 rb_scan_args(argc, argv, "1", &fname);
4533 fstr = FileStringValuePtr(fname);
4534 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4535 retval = MoleculeLoadGamessDatFile(mol, fstr, errbuf, sizeof errbuf);
4536 MyAppCallback_hideProgressPanel();
4538 /* if (retval == -1)
4540 rb_raise(rb_eMolbyError, errbuf);
4547 * savembsf(file) -> bool
4549 * Write structure as a mbsf file. Returns true if successful.
4552 s_Molecule_Savembsf(VALUE self, VALUE fname)
4557 Data_Get_Struct(self, Molecule, mol);
4558 fstr = FileStringValuePtr(fname);
4559 if (MoleculeWriteToMbsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4560 rb_raise(rb_eMolbyError, errbuf);
4566 * savepsf(file) -> bool
4568 * Write structure as a psf file. Returns true if successful.
4571 s_Molecule_Savepsf(VALUE self, VALUE fname)
4576 Data_Get_Struct(self, Molecule, mol);
4577 fstr = FileStringValuePtr(fname);
4578 if (MoleculeWriteToPsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4579 rb_raise(rb_eMolbyError, errbuf);
4585 * savepdb(file) -> bool
4587 * Write coordinates as a pdb file. Returns true if successful.
4590 s_Molecule_Savepdb(VALUE self, VALUE fname)
4595 Data_Get_Struct(self, Molecule, mol);
4596 fstr = FileStringValuePtr(fname);
4597 if (MoleculeWriteToPdbFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4598 rb_raise(rb_eMolbyError, errbuf);
4604 * savedcd(file) -> bool
4606 * Write coordinates as a dcd file. Returns true if successful.
4609 s_Molecule_Savedcd(VALUE self, VALUE fname)
4614 Data_Get_Struct(self, Molecule, mol);
4615 fstr = FileStringValuePtr(fname);
4616 if (MoleculeWriteToDcdFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4617 rb_raise(rb_eMolbyError, errbuf);
4623 * savetep(file) -> bool
4625 * Write coordinates as an ORTEP file. Returns true if successful.
4629 s_Molecule_Savetep(VALUE self, VALUE fname)
4634 Data_Get_Struct(self, Molecule, mol);
4635 fstr = FileStringValuePtr(fname);
4636 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4637 rb_raise(rb_eMolbyError, errbuf);
4642 /* load([ftype, ] fname, ...) */
4644 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
4647 char *argstr, *methname, *p;
4650 const char *ls = (loadFlag ? "load" : "save");
4651 int lslen = strlen(ls);
4656 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
4657 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
4658 if (argstr[0] == ':') {
4659 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
4660 methname = ALLOC_N(char, lslen + strlen(argstr));
4661 strcpy(methname, ls);
4662 strcat(methname, argstr + 1);
4663 for (i = lslen; methname[i] != 0; i++)
4664 methname[i] = tolower(methname[i]);
4665 mid = rb_intern(methname);
4669 rval = rb_funcall2(self, mid, argc, argv);
4671 rb_raise(rb_eMolbyError, "the format specification \'%s\' seems to be wrong", argstr);
4675 /* Guess file type from extension */
4676 p = strrchr(argstr, '.');
4679 for (methname = p; *methname != 0; methname++) {
4680 if (!isalpha(*methname))
4683 if (*methname == 0) {
4684 methname = ALLOC_N(char, lslen + strlen(p) + 1);
4685 if (methname == NULL)
4686 rb_raise(rb_eMolbyError, "Low memory");
4687 strcpy(methname, ls);
4688 strcat(methname, p);
4689 for (i = lslen; methname[i] != 0; i++)
4690 methname[i] = tolower(methname[i]);
4691 mid = rb_intern(methname);
4694 if (rb_respond_to(self, mid)) {
4695 /* Load: try to call the load procedure only if it is available */
4696 rval = rb_funcall2(self, mid, argc, argv);
4701 /* Save: call the save procedure, and if not found then call 'method_missing' */
4702 rval = rb_funcall2(self, mid, argc, argv);
4708 rb_raise(rb_eMolbyError, "the file %s cannot be %s", argstr, (loadFlag ? "loaded" : "saved"));
4712 /* Register the path */
4715 Data_Get_Struct(self, Molecule, mol);
4716 MoleculeSetPath(mol, StringValuePtr(argv[0]));
4718 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
4719 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4720 if (ap->occupancy != 0.0)
4723 if (i == mol->natoms) {
4724 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4725 ap->occupancy = 1.0;
4734 * molload(file, *args) -> bool
4736 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
4737 * file type given by the extension). If this method fails, then all defined (public)
4738 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
4741 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
4743 return s_Molecule_LoadSave(argc, argv, self, 1);
4748 * molsave(file, *args) -> bool
4750 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
4751 * (XXX is the file type given by the extension).
4754 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
4756 return s_Molecule_LoadSave(argc, argv, self, 0);
4763 * Returns the display name of the molecule. If the molecule has no associated
4764 * document, then returns nil.
4767 s_Molecule_Name(VALUE self)
4771 Data_Get_Struct(self, Molecule, mol);
4772 MoleculeCallback_displayName(mol, buf, sizeof buf);
4776 return rb_str_new2(buf);
4781 * set_name(string) -> self
4783 * Set the name of an untitled molecule. If the molecule is not associated with window
4784 * or it already has an associated file, then exception is thrown.
4787 s_Molecule_SetName(VALUE self, VALUE nval)
4790 Data_Get_Struct(self, Molecule, mol);
4791 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
4792 rb_raise(rb_eMolbyError, "Cannot change the window title");
4801 * Returns the full path name of the molecule, if it is associated with a file.
4802 * If the molecule has no associated file, then returns nil.
4805 s_Molecule_Path(VALUE self)
4809 Data_Get_Struct(self, Molecule, mol);
4810 MoleculeCallback_pathName(mol, buf, sizeof buf);
4814 return Ruby_NewFileStringValue(buf);
4821 * Returns the full path name of the directory in which the file associated with the
4822 * molecule is located. If the molecule has no associated file, then returns nil.
4825 s_Molecule_Dir(VALUE self)
4829 Data_Get_Struct(self, Molecule, mol);
4830 MoleculeCallback_pathName(mol, buf, sizeof buf);
4832 translate_char(buf, '\\', '/');
4837 p = strrchr(buf, '/');
4840 return rb_str_new2(buf);
4848 * Returns a string in the form "Molecule[name]" if the molecule has the associated
4849 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
4850 * the Molecule structure) is returned.
4853 s_Molecule_Inspect(VALUE self)
4857 Data_Get_Struct(self, Molecule, mol);
4858 MoleculeCallback_displayName(mol, buf, sizeof buf);
4860 /* No associated document */
4861 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
4862 return rb_str_new2(buf);
4864 /* Check whether the document name is duplicate */
4868 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
4869 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
4870 if (strcmp(buf, buf2) == 0) {
4877 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
4879 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
4881 return rb_str_new2(buf2);
4888 * open(file) -> Molecule
4890 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
4893 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
4899 rb_scan_args(argc, argv, "01", &fname);
4903 p = FileStringValuePtr(fname);
4904 iflag = Ruby_SetInterruptFlag(Qfalse);
4905 mp = MoleculeCallback_openNewMolecule(p);
4906 Ruby_SetInterruptFlag(iflag);
4909 rb_raise(rb_eMolbyError, "Cannot create untitled document");
4911 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
4913 return ValueFromMolecule(mp);
4919 * new(file, *args) -> Molecule
4921 * Create a new molecule and call "load" method with the same arguments.
4924 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
4927 return s_Molecule_Load(argc, argv, self);
4928 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
4932 s_Molecule_MolEnumerable(VALUE self, int kind)
4935 MolEnumerable *mseq;
4936 Data_Get_Struct(self, Molecule, mol);
4937 mseq = MolEnumerableNew(mol, kind);
4938 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
4943 * atoms -> MolEnumerable
4945 * Returns a MolEnumerable object representing the array of atoms.
4948 s_Molecule_Atoms(VALUE self)
4950 return s_Molecule_MolEnumerable(self, kAtomKind);
4955 * bonds -> MolEnumerable
4957 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
4958 * by an array of two atom indices.
4961 s_Molecule_Bonds(VALUE self)
4963 return s_Molecule_MolEnumerable(self, kBondKind);
4968 * angles -> MolEnumerable
4970 * Returns a MolEnumerable object representing the array of angles. An angle is represented
4971 * by an array of three atom indices.
4974 s_Molecule_Angles(VALUE self)
4976 return s_Molecule_MolEnumerable(self, kAngleKind);
4981 * dihedrals -> MolEnumerable
4983 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
4984 * by an array of four atom indices.
4987 s_Molecule_Dihedrals(VALUE self)
4989 return s_Molecule_MolEnumerable(self, kDihedralKind);
4994 * impropers -> MolEnumerable
4996 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
4997 * by an array of four atom indices.
5000 s_Molecule_Impropers(VALUE self)
5002 return s_Molecule_MolEnumerable(self, kImproperKind);
5007 * residues -> MolEnumerable
5009 * Returns a MolEnumerable object representing the array of residue names.
5012 s_Molecule_Residues(VALUE self)
5014 return s_Molecule_MolEnumerable(self, kResidueKind);
5021 * Returns the number of atoms.
5024 s_Molecule_Natoms(VALUE self)
5027 Data_Get_Struct(self, Molecule, mol);
5028 return INT2NUM(mol->natoms);
5035 * Returns the number of bonds.
5038 s_Molecule_Nbonds(VALUE self)
5041 Data_Get_Struct(self, Molecule, mol);
5042 return INT2NUM(mol->nbonds);
5047 * nangles -> Integer
5049 * Returns the number of angles.
5052 s_Molecule_Nangles(VALUE self)
5055 Data_Get_Struct(self, Molecule, mol);
5056 return INT2NUM(mol->nangles);
5061 * ndihedrals -> Integer
5063 * Returns the number of dihedrals.
5066 s_Molecule_Ndihedrals(VALUE self)
5069 Data_Get_Struct(self, Molecule, mol);
5070 return INT2NUM(mol->ndihedrals);
5075 * nimpropers -> Integer
5077 * Returns the number of impropers.
5080 s_Molecule_Nimpropers(VALUE self)
5083 Data_Get_Struct(self, Molecule, mol);
5084 return INT2NUM(mol->nimpropers);
5089 * nresidues -> Integer
5091 * Returns the number of residues.
5094 s_Molecule_Nresidues(VALUE self)
5097 Data_Get_Struct(self, Molecule, mol);
5098 return INT2NUM(mol->nresidues);
5103 * bond_par(idx) -> ParameterRef
5105 * Returns the MD parameter for the idx-th bond.
5108 s_Molecule_BondPar(VALUE self, VALUE val)
5115 Data_Get_Struct(self, Molecule, mol);
5116 ival = NUM2INT(rb_Integer(val));
5117 if (ival < -mol->nbonds || ival >= mol->nbonds)
5118 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5120 ival += mol->nbonds;
5121 s_RebuildMDParameterIfNecessary(self, Qtrue);
5122 i1 = mol->bonds[ival * 2];
5123 i2 = mol->bonds[ival * 2 + 1];
5124 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5125 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5126 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5129 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5134 * angle_par(idx) -> ParameterRef
5136 * Returns the MD parameter for the idx-th angle.
5139 s_Molecule_AnglePar(VALUE self, VALUE val)
5146 Data_Get_Struct(self, Molecule, mol);
5147 ival = NUM2INT(rb_Integer(val));
5148 if (ival < -mol->nangles || ival >= mol->nangles)
5149 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5151 ival += mol->nangles;
5152 s_RebuildMDParameterIfNecessary(self, Qtrue);
5153 i1 = mol->angles[ival * 3];
5154 i2 = mol->angles[ival * 3 + 1];
5155 i3 = mol->angles[ival * 3 + 2];
5156 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5157 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5158 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5159 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5162 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5167 * dihedral_par(idx) -> ParameterRef
5169 * Returns the MD parameter for the idx-th dihedral.
5172 s_Molecule_DihedralPar(VALUE self, VALUE val)
5177 UInt t1, t2, t3, t4;
5179 Data_Get_Struct(self, Molecule, mol);
5180 ival = NUM2INT(rb_Integer(val));
5181 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5182 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5184 ival += mol->ndihedrals;
5185 s_RebuildMDParameterIfNecessary(self, Qtrue);
5186 i1 = mol->dihedrals[ival * 4];
5187 i2 = mol->dihedrals[ival * 4 + 1];
5188 i3 = mol->dihedrals[ival * 4 + 2];
5189 i4 = mol->dihedrals[ival * 4 + 3];
5190 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5191 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5192 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5193 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5194 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5197 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5202 * improper_par(idx) -> ParameterRef
5204 * Returns the MD parameter for the idx-th improper.
5207 s_Molecule_ImproperPar(VALUE self, VALUE val)
5212 UInt t1, t2, t3, t4;
5214 Data_Get_Struct(self, Molecule, mol);
5215 ival = NUM2INT(rb_Integer(val));
5216 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5217 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5219 ival += mol->nimpropers;
5220 s_RebuildMDParameterIfNecessary(self, Qtrue);
5221 i1 = mol->impropers[ival * 4];
5222 i2 = mol->impropers[ival * 4 + 1];
5223 i3 = mol->impropers[ival * 4 + 2];
5224 i4 = mol->impropers[ival * 4 + 3];
5225 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5226 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5227 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5228 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5229 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5232 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5237 * vdw_par(idx) -> ParameterRef
5239 * Returns the vdw parameter for the idx-th atom.
5242 s_Molecule_VdwPar(VALUE self, VALUE val)
5248 Data_Get_Struct(self, Molecule, mol);
5249 ival = NUM2INT(rb_Integer(val));
5250 if (ival < -mol->natoms || ival >= mol->natoms)
5251 rb_raise(rb_eMolbyError, "atom index (%d) out of range", ival);
5253 ival += mol->natoms;
5254 s_RebuildMDParameterIfNecessary(self, Qtrue);
5255 t1 = ATOM_AT_INDEX(mol->atoms, ival)->type;
5256 vp = ParameterLookupVdwPar(mol->par, t1, 0);
5259 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, vp - mol->par->vdwPars);
5264 * start_step -> Integer
5266 * Returns the start step (defined by dcd format).
5269 s_Molecule_StartStep(VALUE self)
5272 Data_Get_Struct(self, Molecule, mol);
5273 return INT2NUM(mol->startStep);
5278 * start_step = Integer
5280 * Set the start step (defined by dcd format).
5283 s_Molecule_SetStartStep(VALUE self, VALUE val)
5286 Data_Get_Struct(self, Molecule, mol);
5287 mol->startStep = NUM2INT(rb_Integer(val));
5293 * steps_per_frame -> Integer
5295 * Returns the number of steps between frames (defined by dcd format).
5298 s_Molecule_StepsPerFrame(VALUE self)
5301 Data_Get_Struct(self, Molecule, mol);
5302 return INT2NUM(mol->stepsPerFrame);
5307 * steps_per_frame = Integer
5309 * Set the number of steps between frames (defined by dcd format).
5312 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5315 Data_Get_Struct(self, Molecule, mol);
5316 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5322 * ps_per_step -> Float
5324 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5327 s_Molecule_PsPerStep(VALUE self)
5330 Data_Get_Struct(self, Molecule, mol);
5331 return rb_float_new(mol->psPerStep);
5336 * ps_per_step = Float
5338 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5341 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5344 Data_Get_Struct(self, Molecule, mol);
5345 mol->psPerStep = NUM2DBL(rb_Float(val));
5351 * find_angles -> Integer
5353 * Find the angles from the bonds. Returns the number of angles newly created.
5357 s_Molecule_FindAngles(VALUE self)
5363 Data_Get_Struct(self, Molecule, mol);
5364 if (mol == NULL || mol->natoms == 0)
5368 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5369 nc = ap->connect.count;
5371 for (i = 0; i < nc; i++) {
5372 n[0] = ap->connects[i];
5373 for (j = i + 1; j < nc; j++) {
5374 n[2] = ap->connects[j];
5375 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5376 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5381 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5384 return INT2NUM(nip);
5389 * find_dihedrals -> Integer
5391 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5395 s_Molecule_FindDihedrals(VALUE self)
5399 int n1, i, j, k, nc1, nc2;
5401 Data_Get_Struct(self, Molecule, mol);
5402 if (mol == NULL || mol->natoms == 0)
5406 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5407 nc1 = ap1->connect.count;
5409 for (i = 0; i < nc1; i++) {
5410 n[2] = ap1->connects[i];
5413 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5414 nc2 = ap2->connect.count;
5415 for (j = 0; j < nc1; j++) {
5416 n[0] = ap1->connects[j];
5419 for (k = 0; k < nc2; k++) {
5420 n[3] = ap2->connects[k];
5421 if (n[3] == n1 || n[3] == n[0])
5423 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5424 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5430 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5433 return INT2NUM(nip);
5439 * nresidues = Integer
5441 * Change the number of residues.
5444 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5447 int ival = NUM2INT(val);
5448 Data_Get_Struct(self, Molecule, mol);
5449 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5450 if (ival != mol->nresidues)
5451 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5457 * max_residue_number(atom_group = nil) -> Integer
5459 * Returns the maximum residue number actually used. If an atom group is given, only
5460 * these atoms are examined. If no atom is present, nil is returned.
5463 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5469 Data_Get_Struct(self, Molecule, mol);
5470 rb_scan_args(argc, argv, "01", &gval);
5471 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5472 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5473 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5478 * min_residue_number(atom_group = nil) -> Integer
5480 * Returns the minimum residue number actually used. If an atom group is given, only
5481 * these atoms are examined. If no atom is present, nil is returned.
5484 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5490 Data_Get_Struct(self, Molecule, mol);
5491 rb_scan_args(argc, argv, "01", &gval);
5492 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5493 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5494 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5499 * each_atom(atom_group = nil) {|aref| ...}
5501 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5502 * group is given, only these atoms are processed.
5503 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5504 * is self (a Molecule object).
5507 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5515 Data_Get_Struct(self, Molecule, mol);
5516 rb_scan_args(argc, argv, "01", &gval);
5517 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5518 arval = ValueFromMoleculeAndIndex(mol, 0);
5519 Data_Get_Struct(arval, AtomRef, aref);
5520 for (i = 0; i < mol->natoms; i++) {
5522 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5526 IntGroupRelease(ig);
5532 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5534 * Returns the unit cell parameters. If cell is not set, returns nil.
5537 s_Molecule_Cell(VALUE self)
5542 Data_Get_Struct(self, Molecule, mol);
5543 if (mol->cell == NULL)
5545 val = rb_ary_new2(6);
5546 for (i = 0; i < 6; i++)
5547 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5548 if (mol->cell->has_sigma) {
5549 for (i = 0; i < 6; i++) {
5550 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5558 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5559 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], flag = nil)
5561 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5562 If the given argument has 12 members, then the second half of the parameters represents the sigma values.
5563 This operation is undoable. If the second argument is given as non-nil, then
5564 the coordinates are transformed so that the fractional coordinates remain the same.
5567 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5573 Data_Get_Struct(self, Molecule, mol);
5574 rb_scan_args(argc, argv, "11", &val, &fval);
5577 MolActionCreateAndPerform(mol, gMolActionSetCell, 0, d, flag);
5578 // MoleculeSetCell(mol, 1, 1, 1, 90, 90, 90);
5581 val = rb_ary_to_ary(val);
5582 if (RARRAY_LEN(val) >= 12) {
5584 } else if (RARRAY_LEN(val) >= 6) {
5586 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5587 for (i = 0; i < n; i++)
5588 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5589 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, flag);
5595 * cell_transform -> Transform
5597 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
5598 * If cell is not defined, nil is returned.
5601 s_Molecule_CellTransform(VALUE self)
5604 Data_Get_Struct(self, Molecule, mol);
5605 if (mol == NULL || mol->cell == NULL)
5607 return ValueFromTransform(&(mol->cell->tr));
5612 * box -> [avec, bvec, cvec, origin, flags]
5614 * Get the unit cell information in the form of a periodic bounding box.
5615 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
5616 * Integers which define whether the system is periodic along the axis.
5617 * If no unit cell is defined, nil is returned.
5620 s_Molecule_Box(VALUE self)
5624 Data_Get_Struct(self, Molecule, mol);
5625 if (mol == NULL || mol->cell == NULL)
5627 v[0] = ValueFromVector(&(mol->cell->axes[0]));
5628 v[1] = ValueFromVector(&(mol->cell->axes[1]));
5629 v[2] = ValueFromVector(&(mol->cell->axes[2]));
5630 v[3] = ValueFromVector(&(mol->cell->origin));
5631 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
5632 val = rb_ary_new4(5, v);
5638 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
5639 * set_box(d, origin = [0, 0, 0])
5642 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
5643 If it is a number, the x/y/z axis vector is multiplied with the given number and used
5645 Flags, if present, is a 3-member array of Integers defining whether the system is
5646 periodic along the axis.
5647 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
5648 In the second form, an isotropic box with cell-length d is set.
5649 In the third form, the existing box is cleared.
5650 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
5653 s_Molecule_SetBox(VALUE self, VALUE aval)
5657 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
5659 Vector origin = {0, 0, 0};
5662 int i, convertCoordinates = 0;
5663 Data_Get_Struct(self, Molecule, mol);
5664 for (i = 0; i < 6; i++) {
5665 if (i < RARRAY_LEN(aval))
5666 v[i] = (RARRAY_PTR(aval))[i];
5670 MolActionCreateAndPerform(mol, gMolActionClearBox);
5673 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
5674 d = NUM2DBL(rb_Float(v[0]));
5675 for (i = 0; i < 3; i++)
5676 VecScale(vv[i], ax[i], d);
5678 VectorFromValue(v[1], &origin);
5679 flags[0] = flags[1] = flags[2] = 1;
5681 for (i = 0; i < 3; i++) {
5684 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
5685 d = NUM2DBL(rb_Float(v[i]));
5686 VecScale(vv[i], ax[i], d);
5688 VectorFromValue(v[i], &vv[i]);
5690 flags[i] = (VecLength2(vv[i]) > 0.0);
5693 VectorFromValue(v[3], &origin);
5695 for (i = 0; i < 3; i++) {
5696 VALUE val = Ruby_ObjectAtIndex(v[4], i);
5697 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
5701 convertCoordinates = 1;
5703 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
5709 * cell_flexibility -> bool
5711 * Returns the unit cell is flexible or not
5714 s_Molecule_CellFlexibility(VALUE self)
5717 Data_Get_Struct(self, Molecule, mol);
5718 if (mol->cell == NULL)
5720 if (mol->useFlexibleCell)
5727 * self.cell_flexibility = bool
5728 * set_cell_flexibility(bool)
5730 * Change the unit cell is flexible or not
5733 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
5736 Data_Get_Struct(self, Molecule, mol);
5737 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
5743 * symmetry -> Array of Transforms
5744 * symmetries -> Array of Transforms
5746 * Get the currently defined symmetry operations. If no symmetry operation is defined,
5747 * returns an empty array.
5750 s_Molecule_Symmetry(VALUE self)
5755 Data_Get_Struct(self, Molecule, mol);
5756 if (mol->nsyms <= 0)
5757 return rb_ary_new();
5758 val = rb_ary_new2(mol->nsyms);
5759 for (i = 0; i < mol->nsyms; i++) {
5760 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
5767 * nsymmetries -> Integer
5769 * Get the number of currently defined symmetry operations.
5772 s_Molecule_Nsymmetries(VALUE self)
5775 Data_Get_Struct(self, Molecule, mol);
5776 return INT2NUM(mol->nsyms);
5781 * add_symmetry(Transform) -> Integer
5783 * Add a new symmetry operation. If no symmetry operation is defined and the
5784 * given argument is not an identity transform, then also add an identity
5785 * transform at the index 0.
5786 * Returns the total number of symmetries after operation.
5789 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
5793 Data_Get_Struct(self, Molecule, mol);
5794 TransformFromValue(trans, &tr);
5795 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
5796 return INT2NUM(mol->nsyms);
5801 * remove_symmetry(count = nil) -> Integer
5802 * remove_symmetries(count = nil) -> Integer
5804 * Remove the specified number of symmetry operations. The last added ones are removed
5805 * first. If count is nil, then all symmetry operations are removed. Returns the
5806 * number of leftover symmetries.
5809 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
5814 Data_Get_Struct(self, Molecule, mol);
5815 rb_scan_args(argc, argv, "01", &cval);
5819 n = NUM2INT(rb_Integer(cval));
5820 if (n < 0 || n > mol->nsyms)
5821 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
5822 if (n == mol->nsyms)
5825 for (i = 0; i < n; i++)
5826 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
5827 return INT2NUM(mol->nsyms);
5831 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5833 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5834 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5835 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5836 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5843 * atom_group {|aref| ...}
5844 * atom_group(arg1, arg2, ...)
5845 * atom_group(arg1, arg2, ...) {|aref| ...}
5847 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5848 * If arguments are given, then the atoms reprensented by the arguments are added to the
5849 * group. For a conversion of a string to an atom index, see the description
5850 * of Molecule#atom_index.
5851 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5852 * representing each atom, and the atoms are removed from the result if the block returns false.
5856 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5858 IntGroup *ig1, *ig2;
5860 Int i, startPt, interval;
5861 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5862 Data_Get_Struct(retval, IntGroup, ig1);
5863 Data_Get_Struct(self, Molecule, mol);
5865 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5868 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5869 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5870 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5871 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5872 ig2 = IntGroupFromValue(*argv);
5873 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5874 interval = IntGroupGetInterval(ig2, i);
5875 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5877 IntGroupRelease(ig2);
5878 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5880 values[0] = (VALUE)mol;
5881 values[1] = (VALUE)ig1;
5882 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5884 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5889 if (rb_block_given_p()) {
5890 /* Evaluate the given block with an AtomRef as the argument, and delete
5891 the index if the block returns false */
5892 AtomRef *aref = AtomRefNew(mol, 0);
5893 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5894 ig2 = IntGroupNew();
5895 IntGroupCopy(ig2, ig1);
5896 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5898 if (startPt >= mol->natoms)
5900 aref->idx = startPt;
5901 resval = rb_yield(arval);
5903 IntGroupRemove(ig1, startPt, 1);
5905 IntGroupRelease(ig2);
5908 /* Remove points that are out of bounds */
5909 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5916 * atom_index(val) -> Integer
5918 * Returns the atom index represented by val. val can be either a non-negative integer
5919 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5920 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
5921 * where resname, resid, name are the residue name, residue id, and atom name respectively.
5922 * If val is a string and multiple atoms match the description, the atom with the lowest index
5926 s_Molecule_AtomIndex(VALUE self, VALUE val)
5929 Data_Get_Struct(self, Molecule, mol);
5930 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5935 * extract(group, dummy_flag = nil) -> Molecule
5937 * Extract the atoms given by group and return as a new molecule object.
5938 * If dummy_flag is true, then the atoms that are not included in the group but are connected
5939 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
5940 * names beginning with an underscore) and included in the new molecule object.
5943 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
5945 Molecule *mol1, *mol2;
5947 VALUE group, dummy_flag, retval;
5948 Data_Get_Struct(self, Molecule, mol1);
5949 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
5950 ig = s_Molecule_AtomGroupFromValue(self, group);
5951 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
5954 retval = ValueFromMolecule(mol2);
5956 IntGroupRelease(ig);
5962 * add(molecule2) -> self
5964 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
5966 This operation is undoable.
5969 s_Molecule_Add(VALUE self, VALUE val)
5971 Molecule *mol1, *mol2;
5972 Data_Get_Struct(self, Molecule, mol1);
5973 mol2 = MoleculeFromValue(val);
5974 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
5980 * remove(group) -> Molecule
5982 * The atoms designated by the given group are removed from the molecule.
5983 * This operation is undoable.
5986 s_Molecule_Remove(VALUE self, VALUE group)
5990 Int *bonds, nbonds, i, temp[2];
5991 IntGroupIterator iter;
5993 Data_Get_Struct(self, Molecule, mol1);
5994 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
5995 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
5996 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
5997 Data_Get_Struct(group, IntGroup, ig);
5999 /* Remove the bonds between the two fragments */
6000 /* (This is necessary for undo to work correctly) */
6003 IntGroupIteratorInit(ig, &iter);
6004 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6005 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6007 cp = AtomConnectData(&ap->connect);
6008 for (j = 0; j < ap->connect.count; j++) {
6010 if (!IntGroupLookup(ig, n, NULL)) {
6011 /* bond i-n, i is in ig and n is not */
6014 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp);
6018 IntGroupIteratorRelease(&iter);
6021 /* temp[0] = kInvalidIndex;
6022 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp); */
6023 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, nbonds * 2, bonds);
6027 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6034 * create_atom(name, pos = -1) -> AtomRef
6036 * Create a new atom with the specified name (may contain residue
6037 * information) and position (if position is out of range, the atom is appended at
6038 * the end). Returns the reference to the new atom.
6039 * This operation is undoable.
6042 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6049 char *p, resName[6], atomName[6];
6051 Data_Get_Struct(self, Molecule, mol);
6052 rb_scan_args(argc, argv, "02", &name, &ival);
6054 pos = NUM2INT(rb_Integer(ival));
6057 p = StringValuePtr(name);
6059 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6060 if (atomName[0] == 0)
6061 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6064 if (p == NULL || p[0] == 0) {
6065 memset(atomName, 0, 4);
6068 memset(&arec, 0, sizeof(arec));
6069 strncpy(arec.aname, atomName, 4);
6071 strncpy(arec.resName, resName, 4);
6072 arec.resSeq = resSeq;
6074 arec.occupancy = 1.0;
6075 // i = MoleculeCreateAnAtom(mol, &arec);
6076 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6078 aref = AtomRefNew(mol, pos);
6079 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6084 * duplicate_atom(atomref, pos = -1) -> AtomRef
6086 * Create a new atom with the same attributes (but no bonding information)
6087 * with the specified atom. Returns the reference to the new atom.
6088 * This operation is undoable.
6091 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6097 VALUE retval, aval, ival;
6099 Data_Get_Struct(self, Molecule, mol);
6100 rb_scan_args(argc, argv, "11", &aval, &ival);
6101 if (FIXNUM_P(aval)) {
6102 int idx = NUM2INT(aval);
6103 if (idx < 0 || idx >= mol->natoms)
6104 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6105 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6107 apsrc = s_AtomFromValue(aval);
6110 rb_raise(rb_eMolbyError, "bad atom specification");
6112 pos = NUM2INT(rb_Integer(ival));
6114 AtomDuplicate(&arec, apsrc);
6115 arec.connect.count = 0;
6116 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6119 aref = AtomRefNew(mol, pos);
6120 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6128 * create_bond(n1, n2, ...) -> Integer
6130 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6131 * do nothing for that pair. Returns the number of bonds actually created.
6132 * This operation is undoable.
6135 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6138 Int i, *ip, old_nbonds;
6140 rb_raise(rb_eMolbyError, "missing arguments");
6142 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6143 Data_Get_Struct(self, Molecule, mol);
6144 ip = ALLOC_N(Int, argc + 1);
6145 for (i = 0; i < argc; i++) {
6146 ip[i] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6148 ip[argc] = kInvalidIndex;
6149 old_nbonds = mol->nbonds;
6150 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, argc, ip);
6153 rb_raise(rb_eMolbyError, "atom index out of range");
6155 rb_raise(rb_eMolbyError, "too many bonds");
6157 rb_raise(rb_eMolbyError, "duplicate bonds");
6159 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6161 rb_raise(rb_eMolbyError, "error in creating bonds");
6162 return INT2NUM(mol->nbonds - old_nbonds);
6167 * molecule.remove_bonds(n1, n2, ...) -> Integer
6169 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6170 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6171 * This operation is undoable.
6174 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6177 Int i, *ip, old_nbonds;
6179 rb_raise(rb_eMolbyError, "missing arguments");
6181 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6182 Data_Get_Struct(self, Molecule, mol);
6183 ip = ALLOC_N(Int, argc + 1);
6184 for (i = 0; i < argc; i++)
6185 ip[i] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6186 ip[argc] = kInvalidIndex;
6187 old_nbonds = mol->nbonds;
6188 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, argc, ip);
6190 return INT2NUM(old_nbonds - mol->nbonds);
6195 * add_angle(n1, n2, n3) -> Molecule
6197 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6198 * when a bond is created, so it is rarely necessary to use this method explicitly.
6199 * This operation is undoable.
6202 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6206 Data_Get_Struct(self, Molecule, mol);
6207 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6208 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6209 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6210 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6211 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6212 n[3] = kInvalidIndex;
6213 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6219 * remove_angle(n1, n2, n3) -> Molecule
6221 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6222 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6223 * This operation is undoable.
6226 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6231 Data_Get_Struct(self, Molecule, mol);
6232 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6233 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6234 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6235 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6236 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6237 ig = IntGroupNewWithPoints(n[3], 1, -1);
6238 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6239 IntGroupRelease(ig);
6245 * add_dihedral(n1, n2, n3, n4) -> Molecule
6247 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6248 * when a bond is created, so it is rarely necessary to use this method explicitly.
6249 * This operation is undoable.
6252 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6256 Data_Get_Struct(self, Molecule, mol);
6257 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6258 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6259 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6260 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6261 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6262 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6263 n[4] = kInvalidIndex;
6264 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6270 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6272 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6273 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6274 * This operation is undoable.
6277 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6282 Data_Get_Struct(self, Molecule, mol);
6283 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6284 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6285 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6286 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6287 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6288 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6289 ig = IntGroupNewWithPoints(n[4], 1, -1);
6290 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6291 IntGroupRelease(ig);
6297 * add_improper(n1, n2, n3, n4) -> Molecule
6299 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6300 * not automatically added when a new bond is created, so this method is more useful than
6301 * the angle/dihedral counterpart.
6302 * This operation is undoable.
6305 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6309 Data_Get_Struct(self, Molecule, mol);
6310 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6311 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6312 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6313 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6314 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6315 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6316 n[4] = kInvalidIndex;
6317 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6323 * remove_improper(n1, n2, n3, n4) -> Molecule
6325 * Remove improper n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6326 * not automatically added when a new bond is created, so this method is more useful than
6327 * the angle/dihedral counterpart.
6328 * This operation is undoable.
6331 s_Molecule_RemoveImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6336 Data_Get_Struct(self, Molecule, mol);
6337 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6338 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6339 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6340 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6341 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6342 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6343 ig = IntGroupNewWithPoints(n[4], 1, -1);
6344 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6345 IntGroupRelease(ig);
6351 * assign_residue(group, res) -> Molecule
6353 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6354 * or "resname.resno". When the residue number is not specified, the residue number of
6355 * the first atom in the group is used.
6356 * This operation is undoable.
6359 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6363 char *p, *pp, buf[16];
6366 Data_Get_Struct(self, Molecule, mol);
6368 /* Parse the argument res */
6369 if (FIXNUM_P(res)) {
6370 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6371 resid = NUM2INT(res);
6374 p = StringValuePtr(res);
6375 pp = strchr(p, '.');
6377 resid = atoi(pp + 1);
6383 if (n > sizeof buf - 1)
6388 ig = s_Molecule_AtomGroupFromValue(self, range);
6389 if (ig == NULL || IntGroupGetCount(ig) == 0)
6393 /* Use the residue number of the first specified atom */
6394 n = IntGroupGetNthPoint(ig, 0);
6395 if (n >= mol->natoms)
6396 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6397 ap = ATOM_AT_INDEX(mol->atoms, n);
6400 /* Change the residue number */
6401 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6402 /* Change the residue name if necessary */
6406 seqs[1] = kInvalidIndex; */
6407 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6409 IntGroupRelease(ig);
6415 * offset_residue(group, offset) -> Molecule
6417 * Offset the residue number of the specified atoms. If any of the residue number gets
6418 * negative, then exception is thrown.
6419 * This operation is undoable.
6422 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6427 Data_Get_Struct(self, Molecule, mol);
6428 ig = s_Molecule_AtomGroupFromValue(self, range);
6429 ofs = NUM2INT(offset);
6430 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6432 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6433 IntGroupRelease(ig);
6439 * renumber_atoms(array) -> IntGroup
6441 * Change the order of atoms so that the atoms specified in the array argument appear
6442 * in this order from the top of the molecule. The atoms that are not included in array
6443 * are placed after these atoms, and these atoms are returned as an intGroup.
6444 * This operation is undoable.
6447 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6453 VALUE *valp, retval;
6454 Data_Get_Struct(self, Molecule, mol);
6455 if (TYPE(array) != T_ARRAY)
6456 array = rb_funcall(array, rb_intern("to_a"), 0);
6457 n = RARRAY_LEN(array);
6458 valp = RARRAY_PTR(array);
6459 new2old = ALLOC_N(Int, n + 1);
6460 for (i = 0; i < n; i++)
6461 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6462 new2old[i] = kInvalidIndex;
6463 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6465 rb_raise(rb_eMolbyError, "Atom index out of range");
6467 rb_raise(rb_eMolbyError, "Duplicate entry");
6469 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6470 retval = IntGroup_Alloc(rb_cIntGroup);
6471 Data_Get_Struct(retval, IntGroup, ig);
6472 if (mol->natoms > n)
6473 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6480 * find_close_atoms(atom, limit = 1.2) -> array of Integers (atom indices)
6482 * Find atoms that are within the threshold distance from the given atom.
6483 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6484 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6485 * If limit is not given, a default value of 1.2 is used.
6486 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
6489 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
6494 Int n1, nbonds, *bonds;
6495 Data_Get_Struct(self, Molecule, mol);
6496 rb_scan_args(argc, argv, "11", &aval, &limval);
6497 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
6501 limit = NUM2DBL(rb_Float(limval));
6502 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
6504 MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
6505 aval = rb_ary_new();
6507 for (n1 = 0; n1 < nbonds; n1++)
6508 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
6516 * guess_bonds(limit = 1.2) -> Integer
6518 * Create bonds between atoms that are within the threshold distance.
6519 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6520 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6521 * If limit is not given, a default value of 1.2 is used.
6522 * The number of the newly created bonds is returned.
6523 * This operation is undoable.
6526 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
6532 Data_Get_Struct(self, Molecule, mol);
6533 rb_scan_args(argc, argv, "01", &limval);
6537 limit = NUM2DBL(rb_Float(limval));
6538 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
6540 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
6543 return INT2NUM(nbonds);
6548 * register_undo(script, *args)
6550 * Register an undo operation with the current molecule.
6553 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6558 Data_Get_Struct(self, Molecule, mol);
6559 rb_scan_args(argc, argv, "1*", &script, &args);
6560 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6561 MolActionCallback_registerUndo(mol, act);
6567 * undo_enabled? -> bool
6569 * Returns true if undo is enabled for this molecule; otherwise no.
6572 s_Molecule_UndoEnabled(VALUE self)
6575 Data_Get_Struct(self, Molecule, mol);
6576 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6583 * undo_enabled = bool
6585 * Enable or disable undo.
6588 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6591 Data_Get_Struct(self, Molecule, mol);
6592 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6598 * selection -> IntGroup
6600 * Returns the current selection.
6603 s_Molecule_Selection(VALUE self)
6608 Data_Get_Struct(self, Molecule, mol);
6609 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6610 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6611 val = ValueFromIntGroup(ig);
6612 IntGroupRelease(ig);
6614 val = IntGroup_Alloc(rb_cIntGroup);
6620 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6624 Data_Get_Struct(self, Molecule, mol);
6628 ig = s_Molecule_AtomGroupFromValue(self, val);
6630 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6632 MoleculeSetSelection(mol, ig);
6634 IntGroupRelease(ig);
6640 * selection = IntGroup
6642 * Set the current selection. The right-hand operand may be nil.
6643 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6646 s_Molecule_SetSelection(VALUE self, VALUE val)
6648 return s_Molecule_SetSelectionSub(self, val, 0);
6653 * set_undoable_selection(IntGroup)
6655 * Set the current selection with undo registration. The right-hand operand may be nil.
6656 * This operation is undoable.
6659 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6661 return s_Molecule_SetSelectionSub(self, val, 1);
6666 * hidden_atoms -> IntGroup
6668 * Returns the currently hidden atoms.
6671 s_Molecule_HiddenAtoms(VALUE self)
6673 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
6674 return Qnil; /* Not reached */
6678 Data_Get_Struct(self, Molecule, mol);
6683 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6684 if (ap->exflags & kAtomHiddenFlag)
6685 IntGroupAdd(ig, i, 1);
6687 val = ValueFromIntGroup(ig);
6688 IntGroupRelease(ig);
6691 } else return Qnil; */
6696 * set_hidden_atoms(IntGroup)
6697 * self.hidden_atoms = IntGroup
6699 * Hide the specified atoms. This operation is _not_ undoable.
6702 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
6704 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
6705 return Qnil; /* Not reached */
6708 Data_Get_Struct(self, Molecule, mol);
6716 ig = s_Molecule_AtomGroupFromValue(self, val);
6717 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6718 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
6719 ap->exflags |= kAtomHiddenFlag;
6721 ap->exflags &= kAtomHiddenFlag;
6725 IntGroupRelease(ig);
6726 MoleculeCallback_notifyModification(mol, 0);
6733 * select_frame(index)
6736 * Select the specified frame. If successful, returns true, otherwise returns false.
6739 s_Molecule_SelectFrame(VALUE self, VALUE val)
6742 int ival = NUM2INT(val);
6743 Data_Get_Struct(self, Molecule, mol);
6744 ival = MoleculeSelectFrame(mol, ival, 1);
6754 * Get the current frame.
6757 s_Molecule_Frame(VALUE self)
6760 Data_Get_Struct(self, Molecule, mol);
6761 return INT2NUM(mol->cframe);
6766 * nframes -> Integer
6768 * Get the number of frames.
6771 s_Molecule_Nframes(VALUE self)
6774 Data_Get_Struct(self, Molecule, mol);
6775 return INT2NUM(MoleculeGetNumberOfFrames(mol));
6780 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
6781 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
6783 * Insert new frames at the indices specified by the intGroup. If the first argument is
6784 * an integer, a single new frame is inserted at that index. If the first argument is
6785 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
6786 * should be an array of arrays of Vector3Ds, then those coordinates are set
6787 * to the new frame. Otherwise, the coordinates of current molecule are copied
6789 * Returns an intGroup representing the inserted frames if successful, nil if not.
6792 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
6794 VALUE val, coords, cells;
6797 int count, ival, i, j, len, len_c, len2, nframes;
6800 Data_Get_Struct(self, Molecule, mol);
6801 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
6802 if (coords != Qnil) {
6803 if (TYPE(coords) != T_ARRAY)
6804 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
6805 len = RARRAY_LEN(coords);
6807 if (cells != Qnil) {
6808 if (mol->cell == NULL)
6809 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
6810 if (TYPE(cells) != T_ARRAY)
6811 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
6812 len_c = RARRAY_LEN(cells);
6814 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
6815 nframes = MoleculeGetNumberOfFrames(mol);
6817 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
6818 val = ValueFromIntGroup(ig);
6820 ig = IntGroupFromValue(val);
6822 count = IntGroupGetCount(ig); /* Count is updated here */
6823 vp = ALLOC_N(Vector, mol->natoms * count);
6825 vp2 = ALLOC_N(Vector, 4 * count);
6829 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
6830 ptr = RARRAY_PTR(coords);
6831 for (i = 0; i < count; i++) {
6832 if (TYPE(ptr[i]) != T_ARRAY)
6833 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
6834 len2 = RARRAY_LEN(ptr[i]);
6835 if (len2 < mol->natoms)
6836 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
6837 ptr2 = RARRAY_PTR(ptr[i]);
6838 for (j = 0; j < mol->natoms; j++)
6839 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
6843 for (i = 0; i < count; i++) {
6844 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
6845 vp[i * mol->natoms + j] = ap->r;
6851 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
6852 ptr = RARRAY_PTR(cells);
6853 for (i = 0; i < count; i++) {
6854 if (TYPE(ptr[i]) != T_ARRAY)
6855 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
6856 len2 = RARRAY_LEN(ptr[i]);
6858 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
6859 ptr2 = RARRAY_PTR(ptr[i]);
6860 for (j = 0; j < 4; j++)
6861 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
6864 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
6865 IntGroupRelease(ig);
6869 return (ival >= 0 ? val : Qnil);
6874 * create_frame(coordinates = nil) -> Integer
6875 * create_frames(coordinates = nil) -> Integer
6877 * Same as molecule.insert_frames(nil, coordinates).
6880 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
6883 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
6885 return s_Molecule_InsertFrames(3, vals, self);
6890 * remove_frames(IntGroup, wantCoordinates = false)
6892 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
6893 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
6894 * removed frames is returned if operation is successful.
6897 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
6904 Data_Get_Struct(self, Molecule, mol);
6905 rb_scan_args(argc, argv, "11", &val, &flag);
6906 ig = IntGroupFromValue(val);
6907 count = IntGroupGetCount(ig);
6909 /* Create return value before removing frames */
6914 retval = rb_ary_new2(count);
6915 for (i = 0; i < count; i++) {
6916 n = IntGroupGetNthPoint(ig, i);
6917 coords = rb_ary_new2(mol->natoms);
6918 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
6919 if (n < ap->nframes && n != mol->cframe)
6922 rb_ary_push(coords, ValueFromVector(&v));
6924 rb_ary_push(retval, coords);
6926 } else retval = Qtrue;
6927 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
6934 * each_frame {|n| ...}
6936 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
6937 * the frame number. After completion, the original frame number is restored.
6940 s_Molecule_EachFrame(VALUE self)
6942 int i, cframe, nframes;
6944 Data_Get_Struct(self, Molecule, mol);
6945 cframe = mol->cframe;
6946 nframes = MoleculeGetNumberOfFrames(mol);
6948 for (i = 0; i < nframes; i++) {
6949 MoleculeSelectFrame(mol, i, 1);
6950 rb_yield(INT2NUM(i));
6952 MoleculeSelectFrame(mol, cframe, 1);
6959 * set_atom_attr(index, key, value)
6961 * Set the atom attribute for the specified atom.
6962 * This operation is undoable.
6965 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
6969 Data_Get_Struct(self, Molecule, mol);
6970 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
6971 oldval = s_AtomRef_GetAttr(aref, key);
6974 s_AtomRef_SetAttr(aref, key, val);
6980 * get_atom_attr(index, key)
6982 * Get the atom attribute for the specified atom.
6985 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
6987 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
6992 * get_coord_from_frame(index, group = nil, cflag = nil)
6994 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
6995 * are modified. If cflag is true, the cell parameter is also copied (if present).
6998 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7001 VALUE ival, gval, cval;
7002 Int index, i, j, n, nn;
7004 IntGroupIterator iter;
7007 Data_Get_Struct(self, Molecule, mol);
7008 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7009 index = NUM2INT(rb_Integer(ival));
7010 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7012 rb_raise(rb_eMolbyError, "No frame is present");
7014 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7017 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7019 ig = s_Molecule_AtomGroupFromValue(self, gval);
7021 n = IntGroupGetCount(ig);
7023 vp = (Vector *)calloc(sizeof(Vector), n);
7024 IntGroupIteratorInit(ig, &iter);
7027 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7028 ap = ATOM_AT_INDEX(mol->atoms, i);
7029 if (index < ap->nframes) {
7030 vp[j] = ap->frames[index];
7038 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7040 if (RTEST(cval) && mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7041 vp = mol->frame_cells + index * 4;
7042 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7044 IntGroupIteratorRelease(&iter);
7046 IntGroupRelease(ig);
7052 * fragment(n1, *exatoms) -> IntGroup
7053 * fragment(group, *exatoms) -> IntGroup
7055 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7056 * those atoms will not be counted during the search.
7059 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7062 IntGroup *baseg, *ig, *exatoms;
7064 volatile VALUE nval, exval;
7065 Data_Get_Struct(self, Molecule, mol);
7066 rb_scan_args(argc, argv, "1*", &nval, &exval);
7067 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7069 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7071 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7073 if (RARRAY_LEN(exval) == 0) {
7076 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7077 Data_Get_Struct(exval, IntGroup, exatoms);
7079 if (baseg == NULL) {
7080 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7082 IntGroupIterator iter;
7083 IntGroupIteratorInit(baseg, &iter);
7084 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7087 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7089 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7091 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7093 IntGroupAddIntGroup(ig, subg);
7094 IntGroupRelease(subg);
7099 IntGroupIteratorRelease(&iter);
7100 IntGroupRelease(baseg);
7103 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7104 nval = ValueFromIntGroup(ig);
7105 IntGroupRelease(ig);
7111 * each_fragment {|group| ...}
7113 * Execute the block, with the IntGroup object for each fragment as the argument.
7114 * Atoms or bonds should not be added or removed during the execution of the block.
7117 s_Molecule_EachFragment(VALUE self)
7122 Data_Get_Struct(self, Molecule, mol);
7123 if (mol == NULL || mol->natoms == 0)
7125 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7126 while (IntGroupGetCount(ag) > 0) {
7127 int n = IntGroupGetNthPoint(ag, 0);
7128 fg = MoleculeFragmentExcludingAtomGroup(mol, n, NULL);
7130 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7131 gval = ValueFromIntGroup(fg);
7133 IntGroupRemoveIntGroup(ag, fg);
7134 IntGroupRelease(fg);
7136 IntGroupRelease(ag);
7142 * detachable?(group) -> [n1, n2]
7144 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7145 * of the molecule via only one bond. If it is, then the indices of the atoms
7146 * belonging to the bond is returned, the first element being the atom included
7147 * in the fragment. Otherwise, Qnil is returned.
7150 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7156 Data_Get_Struct(self, Molecule, mol);
7157 ig = s_Molecule_AtomGroupFromValue(self, gval);
7158 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7159 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7160 } else retval = Qnil;
7161 IntGroupRelease(ig);
7167 * bonds_on_border(group = selection) -> Array of Array of two Integers
7169 * Returns an array of bonds that connect an atom in the group and an atom out
7170 * of the group. The first atom in the bond always belongs to the group. If no
7171 * such bonds are present, an empty array is returned.
7174 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7179 Data_Get_Struct(self, Molecule, mol);
7180 rb_scan_args(argc, argv, "01", &gval);
7182 ig = MoleculeGetSelection(mol);
7186 ig = s_Molecule_AtomGroupFromValue(self, gval);
7188 retval = rb_ary_new();
7191 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7193 IntGroupIterator iter;
7195 IntGroupIteratorInit(bg, &iter);
7196 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7197 /* The atoms at the border */
7199 n1 = mol->bonds[i * 2];
7200 n2 = mol->bonds[i * 2 + 1];
7201 if (IntGroupLookupPoint(ig, n1) < 0) {
7205 if (IntGroupLookupPoint(ig, n1) < 0)
7206 continue; /* Actually this is an internal error */
7208 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7210 IntGroupIteratorRelease(&iter);
7212 IntGroupRelease(bg);
7213 IntGroupRelease(ig);
7219 * translate(vec, group = nil) -> Molecule
7221 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7222 * This operation is undoable.
7225 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7231 Data_Get_Struct(self, Molecule, mol);
7232 rb_scan_args(argc, argv, "11", &vec, &group);
7233 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7234 VectorFromValue(vec, &v);
7235 // MoleculeTranslate(mol, &v, ig);
7236 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7238 IntGroupRelease(ig);
7244 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7246 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7247 * If group is given, only atoms in the group are moved.
7248 * This operation is undoable.
7251 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7254 volatile VALUE aval, anval, cval, gval;
7259 Data_Get_Struct(self, Molecule, mol);
7260 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7261 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7262 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7263 VectorFromValue(aval, &av);
7265 cv.x = cv.y = cv.z = 0.0;
7267 VectorFromValue(cval, &cv);
7268 if (TransformForRotation(tr, &av, angle, &cv))
7269 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7270 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7272 IntGroupRelease(ig);
7278 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7280 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7281 * axis must not be a zero vector.
7282 * If group is given, only atoms in the group are moved.
7283 * This operation is undoable.
7286 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7289 volatile VALUE aval, cval, gval;
7293 Data_Get_Struct(self, Molecule, mol);
7294 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7295 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7296 VectorFromValue(aval, &av);
7298 cv.x = cv.y = cv.z = 0.0;
7300 VectorFromValue(cval, &cv);
7301 if (TransformForReflection(tr, &av, &cv))
7302 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7303 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7305 IntGroupRelease(ig);
7311 * invert(center = [0,0,0], group = nil) -> Molecule
7313 * Invert the molecule with the given center.
7314 * If group is given, only atoms in the group are moved.
7315 * This operation is undoable.
7318 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7321 volatile VALUE cval, gval;
7325 Data_Get_Struct(self, Molecule, mol);
7326 rb_scan_args(argc, argv, "02", &cval, &gval);
7327 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7329 cv.x = cv.y = cv.z = 0.0;
7331 VectorFromValue(cval, &cv);
7332 TransformForInversion(tr, &cv);
7333 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7335 IntGroupRelease(ig);
7341 * transform(transform, group = nil) -> Molecule
7343 * Transform the molecule by the given Transform object.
7344 * If group is given, only atoms in the group are moved.
7345 * This operation is undoable.
7348 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7354 Data_Get_Struct(self, Molecule, mol);
7355 rb_scan_args(argc, argv, "11", &trans, &group);
7356 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7357 TransformFromValue(trans, &tr);
7358 /* MoleculeTransform(mol, tr, ig); */
7359 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7361 IntGroupRelease(ig);
7366 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
7368 switch (MoleculeCenterOfMass(mol, outv, ig)) {
7369 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
7370 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
7372 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
7378 * center_of_mass(group = nil) -> Vector3D
7380 * Calculate the center of mass for the given set of atoms. The argument
7381 * group is null, then all atoms are considered.
7384 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
7390 Data_Get_Struct(self, Molecule, mol);
7391 rb_scan_args(argc, argv, "01", &group);
7392 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7393 s_Molecule_DoCenterOfMass(mol, &v, ig);
7395 IntGroupRelease(ig);
7396 return ValueFromVector(&v);
7401 * centralize(group = nil) -> self
7403 * Translate the molecule so that the center of mass of the given group is located
7404 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7407 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7413 Data_Get_Struct(self, Molecule, mol);
7414 rb_scan_args(argc, argv, "01", &group);
7415 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7416 s_Molecule_DoCenterOfMass(mol, &v, ig);
7418 IntGroupRelease(ig);
7422 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7428 * bounds(group = nil) -> [min, max]
7430 * Calculate the boundary. The return value is an array of two Vector3D objects.
7433 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7441 Data_Get_Struct(self, Molecule, mol);
7442 rb_scan_args(argc, argv, "01", &group);
7443 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7444 if (ig != NULL && IntGroupGetCount(ig) == 0)
7445 rb_raise(rb_eMolbyError, "atom group is empty");
7446 vmin.x = vmin.y = vmin.z = 1e30;
7447 vmax.x = vmax.y = vmax.z = -1e30;
7448 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7450 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7466 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7469 /* Get atom position or a vector */
7471 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7473 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7474 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7475 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7477 VectorFromValue(val, vp);
7483 * measure_bond(n1, n2) -> Float
7485 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7486 * or Vector3D values.
7487 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7490 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7494 Data_Get_Struct(self, Molecule, mol);
7495 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7496 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7497 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7502 * measure_angle(n1, n2, n3) -> Float
7504 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7505 * or Vector3D values. The return value is in degree.
7506 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7509 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7514 Data_Get_Struct(self, Molecule, mol);
7515 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7516 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7517 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7518 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7520 return Qnil; /* Cannot define */
7521 else return rb_float_new(d);
7526 * measure_dihedral(n1, n2, n3, n4) -> Float
7528 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7529 * or Vector3D values. The return value is in degree.
7530 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7533 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7536 Vector v1, v2, v3, v4;
7538 Data_Get_Struct(self, Molecule, mol);
7539 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7540 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7541 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7542 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7543 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7545 return Qnil; /* Cannot define */
7546 else return rb_float_new(d);
7551 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
7553 * Expand the specified part of the molecule by the given symmetry operation.
7554 * Returns the array of atom indices corresponding to the expanded atoms.
7557 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7560 VALUE gval, sval, xval, yval, zval, rval;
7566 Data_Get_Struct(self, Molecule, mol);
7567 rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
7568 n[0] = NUM2INT(rb_Integer(sval));
7569 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7570 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7571 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7572 ig = s_Molecule_AtomGroupFromValue(self, gval);
7573 if (n[0] < 0 || n[0] >= mol->nsyms)
7574 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7575 natoms = mol->natoms;
7577 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
7579 rval = rb_ary_new2(nidx);
7580 while (--nidx >= 0) {
7581 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7583 /* if (natoms == mol->natoms)
7586 rval = IntGroup_Alloc(rb_cIntGroup);
7587 Data_Get_Struct(rval, IntGroup, ig);
7588 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7595 * amend_by_symmetry(group = nil) -> IntGroup
7597 * Expand the specified part of the molecule by the given symmetry operation.
7598 * Returns an IntGroup containing the added atoms.
7601 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7606 Data_Get_Struct(self, Molecule, mol);
7607 rb_scan_args(argc, argv, "01", &gval);
7609 ig = s_Molecule_AtomGroupFromValue(self, gval);
7611 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7612 rval = ValueFromIntGroup(ig2);
7613 IntGroupRelease(ig2);
7619 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7621 * Get the transform corresponding to the symmetry operation. The symop can either be
7622 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7623 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7624 * Otherwise, the returned transform is for fractional coordinates.
7625 * Raises exception when no cell or no transform are defined.
7628 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7634 Data_Get_Struct(self, Molecule, mol);
7635 if (mol->cell == NULL)
7636 rb_raise(rb_eMolbyError, "no unit cell is defined");
7637 if (mol->nsyms == 0)
7638 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7639 rb_scan_args(argc, argv, "11", &sval, &fval);
7640 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7641 symop.sym = NUM2INT(rb_Integer(sval));
7642 symop.dx = symop.dy = symop.dz = 0;
7644 sval = rb_ary_to_ary(sval);
7645 if (RARRAY_LEN(sval) < 4)
7646 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7647 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7648 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7649 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7650 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7652 if (symop.sym >= mol->nsyms)
7653 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7654 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7655 return ValueFromTransform(&tr);
7660 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7662 * Get the symmetry operation corresponding to the given transform.
7663 * If is_cartesian is true, the given transform is for cartesian coordinates.
7664 * Otherwise, the given transform is for fractional coordinates.
7665 * Raises exception when no cell or no transform are defined.
7668 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7675 Data_Get_Struct(self, Molecule, mol);
7676 if (mol->cell == NULL)
7677 rb_raise(rb_eMolbyError, "no unit cell is defined");
7678 if (mol->nsyms == 0)
7679 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7680 rb_scan_args(argc, argv, "11", &tval, &fval);
7681 TransformFromValue(tval, &tr);
7682 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7684 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7686 return Qnil; /* Not found */
7692 * wrap_unit_cell(group) -> Vector3D
7694 * Move the specified group so that the center of mass of the group is within the
7695 * unit cell. The offset vector is returned. If no periodic box is defined,
7696 * exception is raised.
7699 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7704 Data_Get_Struct(self, Molecule, mol);
7705 if (mol->cell == NULL)
7706 rb_raise(rb_eMolbyError, "no unit cell is defined");
7707 ig = s_Molecule_AtomGroupFromValue(self, gval);
7708 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7709 TransformVec(&v, mol->cell->rtr, &cv);
7710 if (mol->cell->flags[0])
7712 if (mol->cell->flags[1])
7714 if (mol->cell->flags[2])
7716 TransformVec(&dv, mol->cell->tr, &v);
7718 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7719 IntGroupRelease(ig);
7720 return ValueFromVector(&dv);
7725 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7727 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7728 * first and second atom in the pair should belong to group1 and group2, respectively.
7729 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7732 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7735 VALUE limval, gval1, gval2, rval, igval;
7736 IntGroup *ig1, *ig2;
7737 IntGroupIterator iter1, iter2;
7743 MDExclusion *exinfo;
7746 Data_Get_Struct(self, Molecule, mol);
7747 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7748 lim = NUM2DBL(rb_Float(limval));
7750 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7752 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7754 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7756 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7758 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7760 if (!RTEST(igval)) {
7761 /* Use the exclusion table in MDArena */
7762 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7763 VALUE mval = ValueFromMolecule(mol);
7764 s_RebuildMDParameterIfNecessary(mval, Qnil);
7766 exinfo = mol->arena->exinfo; /* May be NULL */
7767 exlist = mol->arena->exlist;
7772 IntGroupIteratorInit(ig1, &iter1);
7773 IntGroupIteratorInit(ig2, &iter2);
7776 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7778 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7780 if (exinfo != NULL) {
7781 exn1 = exinfo[n[0]].index1;
7782 exn2 = exinfo[n[0] + 1].index1;
7783 } else exn1 = exn2 = -1;
7784 IntGroupIteratorReset(&iter2);
7785 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7786 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7788 continue; /* Same atom */
7789 if (exinfo != NULL) {
7790 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7791 for (i = exn1; i < exn2; i++) {
7792 if (exlist[i] == n[1])
7796 continue; /* Should be excluded */
7798 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7799 /* Is this pair already registered? */
7801 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7802 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7806 /* Not registered yet */
7807 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7812 IntGroupIteratorRelease(&iter2);
7813 IntGroupIteratorRelease(&iter1);
7814 IntGroupRelease(ig2);
7815 IntGroupRelease(ig1);
7816 rval = rb_ary_new2(npairs);
7817 if (pairs != NULL) {
7818 for (i = 0; i < npairs; i++) {
7819 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7826 /* Calculate the transform that moves the current coordinates to the reference
7827 coordinates with least displacements. */
7829 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
7837 Double eigen_val[3];
7838 Vector eigen_vec[3];
7840 IntGroupIterator iter;
7842 natoms = mol->natoms;
7844 IntGroupIteratorInit(ig, &iter);
7846 /* Calculate the weighted center */
7850 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7851 ap1 = ATOM_AT_INDEX(ap, in);
7852 w1 = (weights != NULL ? weights[i] : ap1->weight);
7853 VecScaleInc(org1, ap1->r, w1);
7854 VecScaleInc(org2, ref[i], w1);
7858 VecScaleSelf(org1, w);
7859 VecScaleSelf(org2, w);
7861 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
7862 /* Matrix to diagonalize = R * tR */
7863 memset(r, 0, sizeof(Mat33));
7864 memset(q, 0, sizeof(Mat33));
7865 memset(u, 0, sizeof(Mat33));
7867 IntGroupIteratorReset(&iter);
7868 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7870 ap1 = ATOM_AT_INDEX(ap, in);
7871 w1 = (weights != NULL ? weights[i] : ap1->weight);
7873 VecSub(v1, ap1->r, org1);
7874 VecSub(v2, ref[i], org2);
7875 r[0] += w1 * v1.x * v2.x;
7876 r[1] += w1 * v1.y * v2.x;
7877 r[2] += w1 * v1.z * v2.x;
7878 r[3] += w1 * v1.x * v2.y;
7879 r[4] += w1 * v1.y * v2.y;
7880 r[5] += w1 * v1.z * v2.y;
7881 r[6] += w1 * v1.x * v2.z;
7882 r[7] += w1 * v1.y * v2.z;
7883 r[8] += w1 * v1.z * v2.z;
7886 for (i = 0; i < 9; i++)
7888 for (i = 0; i < 3; i++) {
7889 for (j = 0; j < 3; j++) {
7890 for (k = 0; k < 3; k++) {
7891 q[i+j*3] += r[i+k*3] * r[j+k*3];
7896 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
7897 IntGroupIteratorRelease(&iter);
7898 return -1.0; /* Cannot determine the eigenvector */
7901 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
7902 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
7903 MatrixTranspose(r, r);
7904 for (i = 0; i < 3; i++) {
7905 MatrixVec(&s[i], r, &eigen_vec[i]);
7906 w1 = 1.0 / sqrt(eigen_val[i]);
7907 VecScaleSelf(s[i], w1);
7909 for (k = 0; k < 3; k++) {
7910 u[0] += s[k].x * eigen_vec[k].x;
7911 u[1] += s[k].y * eigen_vec[k].x;
7912 u[2] += s[k].z * eigen_vec[k].x;
7913 u[3] += s[k].x * eigen_vec[k].y;
7914 u[4] += s[k].y * eigen_vec[k].y;
7915 u[5] += s[k].z * eigen_vec[k].y;
7916 u[6] += s[k].x * eigen_vec[k].z;
7917 u[7] += s[k].y * eigen_vec[k].z;
7918 u[8] += s[k].z * eigen_vec[k].z;
7921 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
7922 MatrixVec(&org1, u, &org1);
7924 for (i = 0; i < 9; i++)
7930 /* Calculate rmsd */
7931 IntGroupIteratorReset(&iter);
7933 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7935 ap1 = ATOM_AT_INDEX(ap, in);
7936 TransformVec(&tv, trans, &ap1->r);
7938 w += VecLength2(tv);
7941 IntGroupIteratorRelease(&iter);
7947 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
7949 * Calculate the transform to fit the given group to the set of reference coordinates.
7950 * The reference coordinates ref is given as either a frame number, an array of
7951 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
7952 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
7953 * Return values are the transform (that converts the present coordinates to the
7954 * target coordinates) and root mean square deviation (without weight).
7957 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
7961 VALUE gval, rval, wval;
7963 IntGroupIterator iter;
7964 int nn, errno, i, j, in, status;
7966 Double *weights, dval[3];
7969 Data_Get_Struct(self, Molecule, mol);
7970 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
7972 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7974 ig = IntGroupFromValue(gval);
7975 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
7976 IntGroupRelease(ig);
7977 rb_raise(rb_eMolbyError, "atom group is not given correctly");
7979 ref = (Vector *)calloc(sizeof(Vector), nn);
7980 weights = (Double *)calloc(sizeof(Double), nn);
7981 IntGroupIteratorInit(ig, &iter);
7982 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
7983 int fn = NUM2INT(rb_Integer(rval));
7984 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
7989 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7990 ap = ATOM_AT_INDEX(mol->atoms, in);
7991 if (fn < ap->nframes)
7992 ref[i] = ap->frames[fn];
7993 else ref[i] = ap->r;
7995 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
7996 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
7997 if (m->row * m->column < nn * 3) {
8001 for (i = 0; i < nn; i++) {
8002 ref[i].x = m->data[i * 3];
8003 ref[i].y = m->data[i * 3 + 1];
8004 ref[i].z = m->data[i * 3 + 2];
8008 rval = rb_protect(rb_ary_to_ary, rval, &status);
8013 if (RARRAY_LEN(rval) < nn) {
8017 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8018 /* Array of 3*nn numbers */
8019 if (RARRAY_LEN(rval) < nn * 3) {
8023 for (i = 0; i < nn; i++) {
8024 for (j = 0; j < 3; j++) {
8025 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8030 dval[j] = NUM2DBL(aval);
8037 /* Array of nn Vector3Ds or Arrays */
8038 for (i = 0; i < nn; i++) {
8039 aval = (RARRAY_PTR(rval))[i];
8040 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8041 VectorFromValue(aval, &ref[i]);
8043 aval = rb_protect(rb_ary_to_ary, aval, &status);
8048 if (RARRAY_LEN(aval) < 3) {
8053 for (j = 0; j < 3; j++) {
8054 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8059 dval[j] = NUM2DBL(aaval);
8069 /* Use atomic weights */
8070 IntGroupIteratorReset(&iter);
8071 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8072 ap = ATOM_AT_INDEX(mol->atoms, in);
8073 weights[i] = ap->weight;
8076 wval = rb_protect(rb_ary_to_ary, wval, &status);
8081 if (RARRAY_LEN(wval) < nn) {
8085 for (i = 0; i < nn; i++) {
8086 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8091 weights[i] = NUM2DBL(wwval);
8094 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8101 IntGroupIteratorRelease(&iter);
8105 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8106 } else if (errno == 1) {
8107 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8108 } else if (errno == 2) {
8109 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8110 } else if (errno == 3) {
8111 rb_jump_tag(status);
8112 } else if (errno == 4) {
8113 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8114 } else if (errno == 5) {
8115 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8116 } else if (errno == 6) {
8117 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8119 return Qnil; /* Not reached */
8126 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8129 s_Molecule_Display(VALUE self)
8132 Data_Get_Struct(self, Molecule, mol);
8133 if (mol->mview != NULL)
8134 MainViewCallback_display(mol->mview);
8142 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8145 s_Molecule_MakeFront(VALUE self)
8148 Data_Get_Struct(self, Molecule, mol);
8149 if (mol->mview != NULL)
8150 MainViewCallback_makeFront(mol->mview);
8156 * update_enabled? -> bool
8158 * Returns true if screen update is enabled; otherwise no.
8161 s_Molecule_UpdateEnabled(VALUE self)
8164 Data_Get_Struct(self, Molecule, mol);
8165 if (mol->mview != NULL && !mol->mview->freezeScreen)
8172 * update_enabled = bool
8174 * Enable or disable screen update. This is effective for automatic update on modification.
8175 * Explicit call to molecule.display() always updates the screen.
8178 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8181 Data_Get_Struct(self, Molecule, mol);
8182 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8183 if (mol->mview != NULL)
8184 mol->mview->freezeScreen = (val == Qfalse);
8192 * show_unitcell(bool)
8193 * show_unitcell = bool
8195 * Set the flag whether to show the unit cell. If no argument is given, the
8196 * current flag is returned.
8199 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8202 Data_Get_Struct(self, Molecule, mol);
8203 if (mol->mview == NULL)
8206 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8207 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8209 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8215 * show_hydrogens(bool)
8216 * show_hydrogens = bool
8218 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8219 * current flag is returned.
8222 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8225 Data_Get_Struct(self, Molecule, mol);
8226 if (mol->mview == NULL)
8229 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8230 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8232 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8238 * show_dummy_atoms(bool)
8239 * show_dummy_atoms = bool
8241 * Set the flag whether to show the dummy atoms. If no argument is given, the
8242 * current flag is returned.
8245 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8248 Data_Get_Struct(self, Molecule, mol);
8249 if (mol->mview == NULL)
8252 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8253 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8255 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8261 * show_expanded(bool)
8262 * show_expanded = bool
8264 * Set the flag whether to show the expanded atoms. If no argument is given, the
8265 * current flag is returned.
8268 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8271 Data_Get_Struct(self, Molecule, mol);
8272 if (mol->mview == NULL)
8275 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8276 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8278 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8284 * show_ellipsoids(bool)
8285 * show_ellipsoids = bool
8287 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8288 * current flag is returned.
8291 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8294 Data_Get_Struct(self, Molecule, mol);
8295 if (mol->mview == NULL)
8298 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8299 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8301 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8306 * is_atom_visible(index) -> Boolean
8308 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8309 * as well as the molecule attributes (showHydrogens, etc.)
8312 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8317 Data_Get_Struct(self, Molecule, mol);
8318 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8319 if (idx < 0 || idx >= mol->natoms)
8321 ap = ATOM_AT_INDEX(mol->atoms, idx);
8322 if (mol->mview != NULL) {
8323 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8325 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8327 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8330 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8335 * show_graphite -> Integer
8336 * show_graphite = Integer
8337 * show_graphite = boolean
8339 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8340 * number of rings to display for each direction.
8341 * If the argument is boolean, only the show/hide flag is set.
8344 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8347 Data_Get_Struct(self, Molecule, mol);
8348 if (mol->mview == NULL)
8351 if (argv[0] == Qnil || argv[0] == Qfalse)
8352 mol->mview->showGraphiteFlag = 0;
8353 else if (argv[0] == Qtrue)
8354 mol->mview->showGraphiteFlag = 1;
8356 int n = NUM2INT(rb_Integer(argv[0]));
8358 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
8359 mol->mview->showGraphite = n;
8361 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8363 return INT2NUM(mol->mview->showGraphite);
8368 * show_graphite? -> boolean
8370 * Return whether the graphite is set visible or not.
8373 s_Molecule_ShowGraphiteFlag(VALUE self)
8376 Data_Get_Struct(self, Molecule, mol);
8377 if (mol->mview == NULL)
8379 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
8384 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
8385 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
8386 * show_periodic_image = boolean
8388 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
8389 * set but no visual effects are observed.
8390 * If the argument is boolean, only the show/hide flag is modified.
8393 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
8399 Data_Get_Struct(self, Molecule, mol);
8400 if (mol->mview == NULL)
8402 rb_scan_args(argc, argv, "01", &val);
8404 /* Change current settings */
8405 if (val == Qnil || val == Qfalse)
8406 mol->mview->showPeriodicImageFlag = 0;
8407 else if (val == Qtrue)
8408 mol->mview->showPeriodicImageFlag = 1;
8410 val = rb_ary_to_ary(val);
8411 for (i = 0; i < 6; i++) {
8412 if (i < RARRAY_LEN(val))
8413 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
8415 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
8416 rb_raise(rb_eMolbyError, "bad arguments");
8417 for (i = 0; i < 6; i++)
8418 mol->mview->showPeriodicImage[i] = ival[i];
8420 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8423 for (i = 0; i < 6; i++)
8424 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
8430 * show_periodic_image? -> boolean
8432 * Return whether the periodic images are set to visible or not. This flag is
8433 * independent from the show_periodic_image settings.
8436 s_Molecule_ShowPeriodicImageFlag(VALUE self)
8439 Data_Get_Struct(self, Molecule, mol);
8440 if (mol->mview == NULL)
8442 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
8451 * Set the flag whether to draw the model in line mode. If no argument is given, the
8452 * current flag is returned.
8455 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
8458 Data_Get_Struct(self, Molecule, mol);
8459 if (mol->mview == NULL)
8462 mol->mview->lineMode = (RTEST(argv[0]) != 0);
8463 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8465 return (mol->mview->lineMode ? Qtrue : Qfalse);
8472 * Resize the model drawing to fit in the window.
8475 s_Molecule_ResizeToFit(VALUE self)
8478 Data_Get_Struct(self, Molecule, mol);
8479 if (mol->mview != NULL)
8480 MainView_resizeToFit(mol->mview);
8486 * get_view_rotation -> [[ax, ay, az], angle]
8488 * Get the current rotation for the view. Angle is in degree, not radian.
8491 s_Molecule_GetViewRotation(VALUE self)
8496 Data_Get_Struct(self, Molecule, mol);
8497 if (mol->mview == NULL)
8499 TrackballGetRotate(mol->mview->track, f);
8500 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
8504 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
8509 * get_view_scale -> float
8511 * Get the current scale for the view.
8514 s_Molecule_GetViewScale(VALUE self)
8517 Data_Get_Struct(self, Molecule, mol);
8518 if (mol->mview == NULL)
8520 return rb_float_new(TrackballGetScale(mol->mview->track));
8525 * get_view_center -> Vector
8527 * Get the current center point of the view.
8530 s_Molecule_GetViewCenter(VALUE self)
8535 Data_Get_Struct(self, Molecule, mol);
8536 if (mol->mview == NULL)
8538 TrackballGetTranslate(mol->mview->track, f);
8539 v.x = -f[0] * mol->mview->dimension;
8540 v.y = -f[1] * mol->mview->dimension;
8541 v.z = -f[2] * mol->mview->dimension;
8542 return ValueFromVector(&v);
8547 * set_view_rotation([ax, ay, az], angle) -> self
8549 * Set the current rotation for the view. Angle is in degree, not radian.
8552 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
8557 Data_Get_Struct(self, Molecule, mol);
8558 if (mol->mview == NULL)
8560 VectorFromValue(aval, &v);
8561 if (NormalizeVec(&v, &v))
8562 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
8566 f[0] = NUM2DBL(rb_Float(angval));
8567 TrackballSetRotate(mol->mview->track, f);
8568 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8574 * set_view_scale(scale) -> self
8576 * Set the current scale for the view.
8579 s_Molecule_SetViewScale(VALUE self, VALUE aval)
8582 Data_Get_Struct(self, Molecule, mol);
8583 if (mol->mview == NULL)
8585 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
8586 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8592 * set_view_center(vec) -> self
8594 * Set the current center point of the view.
8597 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
8602 Data_Get_Struct(self, Molecule, mol);
8603 if (mol->mview == NULL)
8605 VectorFromValue(aval, &v);
8606 f[0] = -v.x / mol->mview->dimension;
8607 f[1] = -v.y / mol->mview->dimension;
8608 f[2] = -v.z / mol->mview->dimension;
8609 TrackballSetTranslate(mol->mview->track, f);
8610 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8616 * set_background_color(red, green, blue)
8618 * Set the background color of the model window.
8621 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
8624 Data_Get_Struct(self, Molecule, mol);
8625 if (mol->mview != NULL) {
8626 VALUE rval, gval, bval;
8627 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
8628 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
8635 * create_graphic(kind, color, points, fill = nil) -> integer
8637 * Create a new graphic object.
8638 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
8639 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
8640 * points: an array of Vectors
8644 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
8650 VALUE kval, cval, pval, fval;
8651 Data_Get_Struct(self, Molecule, mol);
8652 if (mol->mview == NULL)
8653 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8654 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
8655 kval = rb_obj_as_string(kval);
8656 memset(&g, 0, sizeof(g));
8658 p = RSTRING_PTR(kval);
8659 if (strcmp(p, "line") == 0)
8660 g.kind = kMainViewGraphicLine;
8661 else if (strcmp(p, "poly") == 0)
8662 g.kind = kMainViewGraphicPoly;
8663 else if (strcmp(p, "cylinder") == 0)
8664 g.kind = kMainViewGraphicCylinder;
8665 else if (strcmp(p, "cone") == 0)
8666 g.kind = kMainViewGraphicCone;
8667 else if (strcmp(p, "ellipsoid") == 0)
8668 g.kind = kMainViewGraphicEllipsoid;
8669 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
8670 g.closed = (RTEST(fval) ? 1 : 0);
8671 cval = rb_ary_to_ary(cval);
8672 n = RARRAY_LEN(cval);
8673 if (n < 3 || n >= 5)
8674 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
8677 for (i = 0; i < n; i++)
8678 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
8679 pval = rb_ary_to_ary(pval);
8680 n = RARRAY_LEN(pval);
8681 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
8683 rb_raise(rb_eArgError, "no control points are given");
8685 case kMainViewGraphicLine:
8687 rb_raise(rb_eArgError, "the line object must have at least two control points");
8689 case kMainViewGraphicPoly:
8691 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
8693 case kMainViewGraphicCylinder:
8694 case kMainViewGraphicCone:
8696 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
8699 case kMainViewGraphicEllipsoid:
8703 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
8706 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
8707 for (i = 0; i < n; i++) {
8710 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
8713 VectorFromValue(RARRAY_PTR(pval)[i], &v);
8715 g.points[i * 3] = v.x;
8716 g.points[i * 3 + 1] = v.y;
8717 g.points[i * 3 + 2] = v.z;
8719 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
8721 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
8722 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
8723 g.points[7] = g.points[11] = g.points[3];
8725 MainView_insertGraphic(mol->mview, -1, &g);
8726 return INT2NUM(mol->mview->ngraphics - 1);
8731 * remove_graphic(index) -> integer
8733 * Remove a graphic object.
8736 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
8740 Data_Get_Struct(self, Molecule, mol);
8741 if (mol->mview == NULL)
8742 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8743 i = NUM2INT(rb_Integer(ival));
8744 if (i < 0 || i >= mol->mview->ngraphics)
8745 rb_raise(rb_eArgError, "graphic index is out of range");
8746 MainView_removeGraphic(mol->mview, i);
8752 * ngraphics -> integer
8754 * Get the number of graphic objects.
8757 s_Molecule_NGraphics(VALUE self)
8760 Data_Get_Struct(self, Molecule, mol);
8761 if (mol->mview == NULL)
8762 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8763 return INT2NUM(mol->mview->ngraphics);
8768 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
8770 * Change the point_index-th control point of graphic_index-th graphic object
8774 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
8776 MainViewGraphic *gp;
8780 Data_Get_Struct(self, Molecule, mol);
8781 if (mol->mview == NULL)
8782 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8783 index = NUM2INT(rb_Integer(gval));
8784 if (index < 0 || index >= mol->mview->ngraphics)
8785 rb_raise(rb_eArgError, "the graphic index is out of range");
8786 gp = mol->mview->graphics + index;
8787 index = NUM2INT(rb_Integer(pval));
8788 if (index < 0 || index >= gp->npoints)
8789 rb_raise(rb_eArgError, "the point index is out of range");
8790 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
8791 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
8792 v.x = NUM2DBL(rb_Float(nval));
8794 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
8795 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
8796 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
8798 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
8801 v.x = kInvalidFloat;
8803 } else VectorFromValue(nval, &v);
8805 gp->points[index * 3] = v.x;
8806 gp->points[index * 3 + 1] = v.y;
8807 gp->points[index * 3 + 2] = v.z;
8808 MoleculeCallback_notifyModification(mol, 0);
8814 * set_graphic_color(graphic_index, new_value) -> new_value
8816 * Change the color of graphic_index-th graphic object
8820 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
8822 MainViewGraphic *gp;
8825 Data_Get_Struct(self, Molecule, mol);
8826 if (mol->mview == NULL)
8827 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8828 index = NUM2INT(rb_Integer(gval));
8829 if (index < 0 || index >= mol->mview->ngraphics)
8830 rb_raise(rb_eArgError, "the graphic index is out of range");
8831 gp = mol->mview->graphics + index;
8832 cval = rb_ary_to_ary(cval);
8833 n = RARRAY_LEN(cval);
8834 if (n != 3 && n != 4)
8835 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
8836 for (index = 0; index < n; index++) {
8837 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
8841 MoleculeCallback_notifyModification(mol, 0);
8847 * show_graphic(graphic_index) -> self
8849 * Enable the visible flag of the graphic_index-th graphic object
8853 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
8855 MainViewGraphic *gp;
8858 Data_Get_Struct(self, Molecule, mol);
8859 if (mol->mview == NULL)
8860 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8861 index = NUM2INT(rb_Integer(gval));
8862 if (index < 0 || index >= mol->mview->ngraphics)
8863 rb_raise(rb_eArgError, "the graphic index is out of range");
8864 gp = mol->mview->graphics + index;
8866 MoleculeCallback_notifyModification(mol, 0);
8872 * hide_graphic(graphic_index) -> self
8874 * Disable the visible flag of the graphic_index-th graphic object
8878 s_Molecule_HideGraphic(VALUE self, VALUE gval)
8880 MainViewGraphic *gp;
8883 Data_Get_Struct(self, Molecule, mol);
8884 if (mol->mview == NULL)
8885 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8886 index = NUM2INT(rb_Integer(gval));
8887 if (index < 0 || index >= mol->mview->ngraphics)
8888 rb_raise(rb_eArgError, "the graphic index is out of range");
8889 gp = mol->mview->graphics + index;
8891 MoleculeCallback_notifyModification(mol, 0);
8899 * Show the string in the info text box.
8902 s_Molecule_ShowText(VALUE self, VALUE arg)
8905 Data_Get_Struct(self, Molecule, mol);
8906 if (mol->mview != NULL)
8907 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
8913 * md_arena -> MDArena
8915 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
8916 * this molecule, a new arena is created.
8919 s_Molecule_MDArena(VALUE self)
8923 Data_Get_Struct(self, Molecule, mol);
8924 if (mol->arena == NULL)
8926 retval = ValueFromMDArena(mol->arena);
8932 * set_parameter_attr(type, index, key, value, src) -> value
8934 * This method is used only internally.
8937 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
8939 /* This method is called from MolAction to change a MM parameter attribute. */
8944 Data_Get_Struct(self, Molecule, mol);
8945 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
8946 vval = s_ParameterRef_SetAttr(pval, kval, vval);
8948 /* This is the special part of this method; it allows modification of the src field. */
8949 /* (ParameterRef#set_attr sets 0 to the src field) */
8950 Data_Get_Struct(pval, ParameterRef, pref);
8951 up = ParameterRefGetPar(pref);
8952 up->bond.src = FIX2INT(sval);
8959 * parameter -> Parameter
8961 * Get the local parameter of this molecule. If not defined, returns nil.
8964 s_Molecule_Parameter(VALUE self)
8967 Data_Get_Struct(self, Molecule, mol);
8968 /* if (mol->par == NULL)
8970 return s_NewParameterValueFromValue(self);
8975 * selectedMO -> IntGroup
8977 * Returns a group of selected mo in the "MO Info" table. If the MO info table
8978 * is not selected, returns nil. If the MO info table is selected but no MOs
8979 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
8982 s_Molecule_SelectedMO(VALUE self)
8987 Data_Get_Struct(self, Molecule, mol);
8988 if (mol->mview == NULL)
8990 ig = MainView_selectedMO(mol->mview);
8993 IntGroupOffset(ig, 1);
8994 val = ValueFromIntGroup(ig);
8995 IntGroupRelease(ig);
9001 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9003 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9004 * If the molecule does not contain a basis set information, then returns nil.
9007 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9010 Vector o, dx, dy, dz;
9013 Int npoints = 80 * 80 * 80;
9014 Data_Get_Struct(self, Molecule, mol);
9015 if (mol->bset == NULL)
9017 rb_scan_args(argc, argv, "01", &nval);
9019 npoints = NUM2INT(rb_Integer(nval));
9020 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9022 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));
9026 s_Cubegen_callback(double progress, void *ref)
9028 MyAppCallback_setProgressValue(progress);
9029 if (MyAppCallback_checkInterrupt())
9036 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9037 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9039 * Calculate the molecular orbital with number mo and create a 'cube' file.
9040 * In the first form, the cube size is estimated from the atomic coordinates. In the
9041 * second form, the cube dimension is explicitly given.
9042 * Returns fname when successful, nil otherwise.
9043 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9044 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9045 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9048 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9050 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9052 Int mono, nx, ny, nz, npoints;
9053 Vector o, dx, dy, dz;
9056 Data_Get_Struct(self, Molecule, mol);
9057 if (mol->bset == NULL)
9058 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9059 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9061 /* Set up parameters */
9062 mono = NUM2INT(rb_Integer(mval));
9063 if (mono <= 0 || mono > mol->bset->ncomps)
9064 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9066 if (mol->bset->rflag != 0)
9067 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9068 mono += mol->bset->ncomps;
9071 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9072 /* Automatic grid formation */
9074 npoints = NUM2INT(rb_Integer(oval));
9078 else if (npoints < 8)
9079 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9080 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9081 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9085 VectorFromValue(oval, &o);
9086 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9087 VectorFromValue(dxval, &dx);
9089 dx.x = NUM2DBL(rb_Float(dxval));
9092 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9093 VectorFromValue(dyval, &dy);
9095 dy.y = NUM2DBL(rb_Float(dyval));
9098 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9099 VectorFromValue(dzval, &dz);
9101 dz.z = NUM2DBL(rb_Float(dzval));
9104 nx = NUM2INT(rb_Integer(nxval));
9105 ny = NUM2INT(rb_Integer(nyval));
9106 nz = NUM2INT(rb_Integer(nzval));
9107 if (nx <= 0 || ny <= 0 || nz <= 0)
9108 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9109 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9110 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);
9114 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9118 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9120 /* Output to file */
9121 MoleculeCallback_displayName(mol, buf, sizeof buf);
9122 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9124 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9126 /* Discard the cube */
9127 MoleculeClearCubeAtIndex(mol, index);
9135 * Get the number of electrostatic potential info.
9138 s_Molecule_NElpots(VALUE self)
9141 Data_Get_Struct(self, Molecule, mol);
9142 return INT2NUM(mol->nelpots);
9149 * Get the electrostatic potential info at the given index. If present, then the
9150 * return value is [Vector, Float] (position and potential). If not present, then
9154 s_Molecule_Elpot(VALUE self, VALUE ival)
9158 Data_Get_Struct(self, Molecule, mol);
9159 idx = NUM2INT(rb_Integer(ival));
9160 if (idx < 0 || idx >= mol->nelpots)
9162 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9167 * add_gaussian_orbital_shell(sym, nprims, atom_index)
9169 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9170 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9174 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9177 int sym, nprims, a_idx, n;
9178 Data_Get_Struct(self, Molecule, mol);
9179 sym = NUM2INT(rb_Integer(symval));
9180 nprims = NUM2INT(rb_Integer(npval));
9181 a_idx = NUM2INT(rb_Integer(aval));
9182 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9184 rb_raise(rb_eMolbyError, "Molecule is emptry");
9186 rb_raise(rb_eMolbyError, "Low memory");
9188 rb_raise(rb_eMolbyError, "Unknown orbital type");
9190 rb_raise(rb_eMolbyError, "Unknown error");
9196 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9198 * To be used internally. Add a gaussian primitive coefficients.
9201 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9205 Double exponent, contraction, contraction_sp;
9206 Data_Get_Struct(self, Molecule, mol);
9207 exponent = NUM2DBL(rb_Float(expval));
9208 contraction = NUM2DBL(rb_Float(cval));
9209 contraction_sp = NUM2DBL(rb_Float(cspval));
9210 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9212 rb_raise(rb_eMolbyError, "Molecule is emptry");
9214 rb_raise(rb_eMolbyError, "Low memory");
9216 rb_raise(rb_eMolbyError, "Unknown error");
9224 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9227 s_Molecule_MOType(VALUE self)
9230 Data_Get_Struct(self, Molecule, mol);
9231 if (mol != NULL && mol->bset != NULL) {
9233 int rflag = mol->bset->rflag;
9236 else if (rflag == 2)
9239 return rb_str_new2(s);
9245 * set_mo_coefficients(idx, energy, coefficients)
9247 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
9248 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9249 * of MO coefficients.
9252 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9258 Data_Get_Struct(self, Molecule, mol);
9259 idx = NUM2INT(rb_Integer(ival));
9260 energy = NUM2DBL(rb_Float(eval));
9261 aval = rb_ary_to_ary(aval);
9262 ncomps = RARRAY_LEN(aval);
9263 coeffs = (Double *)calloc(sizeof(Double), ncomps);
9264 if (coeffs == NULL) {
9268 for (i = 0; i < ncomps; i++)
9269 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9270 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9273 rb_raise(rb_eMolbyError, "Molecule is emptry");
9275 rb_raise(rb_eMolbyError, "Low memory");
9277 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9279 rb_raise(rb_eMolbyError, "Bad MO index");
9281 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9283 rb_raise(rb_eMolbyError, "Unknown error");
9289 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9291 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9292 * ne_alpha, ne_beta: number of alpha/beta electrons.
9295 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9298 Int rflag, na, nb, n;
9299 Data_Get_Struct(self, Molecule, mol);
9300 rflag = NUM2INT(rb_Integer(rval));
9301 na = NUM2INT(rb_Integer(naval));
9302 nb = NUM2INT(rb_Integer(nbval));
9303 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9305 rb_raise(rb_eMolbyError, "Molecule is emptry");
9307 rb_raise(rb_eMolbyError, "Low memory");
9309 rb_raise(rb_eMolbyError, "Unknown error");
9315 * search_equivalent_atoms(ig = nil)
9317 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
9320 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
9326 Data_Get_Struct(self, Molecule, mol);
9327 if (mol->natoms == 0)
9329 rb_scan_args(argc, argv, "01", &val);
9331 ig = IntGroupFromValue(val);
9333 result = MoleculeSearchEquivalentAtoms(mol, ig);
9335 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
9337 IntGroupRelease(ig);
9338 val = rb_ary_new2(mol->natoms);
9339 for (i = 0; i < mol->natoms; i++)
9340 rb_ary_push(val, INT2NUM(result[i]));
9347 * create_pi_anchor(name, type, group [, weights]) -> index
9348 * insert_pi_anchor(index, name, type, group [, weights]) -> index
9349 * replace_pi_anchor(index, name, type, group [, weights]) -> index
9351 * Create or replace a "pi anchor", which is an anchor point to define a metal-pi bond.
9352 * If index is negative or no less than the current number of pi anchors, create
9353 * a new anchor. Otherwise, replace the existing anchor.
9354 * Name and type are Strings, and are similar to those in atoms.
9355 * Group is a group of atoms to define a pi system to be bound to the metal.
9356 * Weights (optional) is an Array of Floats, which determine the significance
9357 * of the component atoms. If not given, then 1.0/N (N is the number of atoms
9358 * in the group) is assumed for all atoms.
9361 s_Molecule_CreateOrReplacePiAnchor(int func, int argc, VALUE *argv, VALUE self)
9364 Int idx, i, j, atype, *ip;
9365 VALUE ival, nval, tval, gval, wval;
9366 char *np, *tp, aname[6];
9369 Data_Get_Struct(self, Molecule, mol);
9370 if (func == 0) { /* create */
9371 rb_scan_args(argc, argv, "31", &nval, &tval, &gval, &wval);
9372 idx = mol->npiatoms;
9374 rb_scan_args(argc, argv, "41", &ival, &nval, &tval, &gval, &wval);
9375 idx = NUM2INT(rb_Integer(ival));
9376 if (idx < 0 || idx > mol->npiatoms || (func == 2 && idx == mol->npiatoms))
9377 rb_raise(rb_eMolbyError, "pi anchor index out of range");
9379 np = StringValuePtr(nval);
9380 strncpy(aname, np, 4);
9381 tp = StringValuePtr(tval);
9382 atype = AtomTypeEncodeToUInt(tp);
9383 ig = IntGroupFromValue(gval);
9384 if ((i = IntGroupGetCount(ig)) < 2)
9385 rb_raise(rb_eMolbyError, "too few atoms are given (at least 2 are needed)");
9386 ip = (Int *)malloc(sizeof(Int) * i);
9387 for (i = 0; (j = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
9389 if (j < 0 || j >= mol->natoms) {
9391 rb_raise(rb_eMolbyError, "the atom index (%d) is out of range", j);
9394 dp = (Double *)malloc(sizeof(Double) * i);
9396 wval = rb_ary_to_ary(wval);
9397 if (RARRAY_LEN(wval) < i) {
9400 rb_raise(rb_eMolbyError, "the number of floats is not sufficient (%d needed but only %d given)", i, RARRAY_LEN(wval));
9402 for (j = 0; j < i; j++) {
9403 dp[j] = NUM2DBL(rb_Float(RARRAY_PTR(wval)[j]));
9406 for (j = 0; j < i; j++) {
9410 if (func == 0 || func == 1) /* Create or insert */
9411 MolActionCreateAndPerform(mol, gMolActionInsertOnePiAtom, idx, 4, aname, atype, i, ip, i, dp);
9413 MolActionCreateAndPerform(mol, gMolActionReplaceOnePiAtom, idx, 4, aname, atype, i, ip, i, dp);
9416 return INT2NUM(idx);
9420 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
9422 return s_Molecule_CreateOrReplacePiAnchor(0, argc, argv, self);
9426 s_Molecule_InsertPiAnchor(int argc, VALUE *argv, VALUE self)
9428 return s_Molecule_CreateOrReplacePiAnchor(1, argc, argv, self);
9432 s_Molecule_ReplacePiAnchor(int argc, VALUE *argv, VALUE self)
9434 return s_Molecule_CreateOrReplacePiAnchor(2, argc, argv, self);
9439 * remove_pi_anchor(idx) -> Molecule
9441 * Remove the pi-anchor at the given index.
9444 s_Molecule_RemovePiAnchor(VALUE self, VALUE ival)
9447 Int idx = NUM2INT(rb_Integer(ival));
9448 Data_Get_Struct(self, Molecule, mol);
9449 if (idx < 0 || idx >= mol->npiatoms)
9450 rb_raise(rb_eMolbyError, "pi-anchor index (%d) is out of range (should be 0 <= index < %d)", idx, mol->npiatoms);
9451 MolActionCreateAndPerform(mol, gMolActionRemoveOnePiAtom, idx);
9457 * pi_anchor(idx) -> nil or [name, type, group, weights]
9459 * Return the attributes of the idx-th pi anchor if present, otherwise nil.
9462 s_Molecule_PiAnchorAtIndex(VALUE self, VALUE ival)
9466 char aname[6], atype[8];
9468 VALUE cval, wval, rval;
9469 Int idx = NUM2INT(rb_Integer(ival));
9470 Data_Get_Struct(self, Molecule, mol);
9471 if (idx < 0 || idx >= mol->npiatoms)
9473 pp = mol->piatoms + idx;
9474 strncpy(aname, pp->aname, 4);
9476 AtomTypeDecodeToString(pp->type, atype);
9477 cval = rb_ary_new2(pp->connect.count);
9478 connects = AtomConnectData(&pp->connect);
9479 for (i = 0; i < pp->connect.count; i++)
9480 rb_ary_store(cval, i, INT2NUM(connects[i]));
9481 wval = rb_ary_new2(pp->ncoeffs);
9482 for (i = 0; i < pp->ncoeffs; i++)
9483 rb_ary_store(wval, i, rb_float_new(pp->coeffs[i]));
9484 rval = rb_ary_new3(4, rb_str_new2(aname), rb_str_new2(atype), cval, wval);
9490 * count_pi_anchors -> Integer
9492 * Return the number of defined pi anchors.
9495 s_Molecule_CountPiAnchors(VALUE self)
9498 Data_Get_Struct(self, Molecule, mol);
9499 return INT2NUM(mol->npiatoms);
9504 * create_pi_anchor_construct(n1, n2, n3 = nil, n4 = nil) -> Integer
9506 * Create a bond, angle, or dihedral including one or more pi anchor points.
9507 * The arguments can either be an atom representation (atom index, atom name, res_seq:name)
9508 * or a pi-anchor representation (pi-anchor index, pi-anchor name).
9509 * The pi-anchor index is represented by a negative integer -N, which corresponds to
9510 * the (N-1)-th pi anchor.
9511 * Returns the index for the newly created construct.
9512 * This operation is undoable.
9515 s_Molecule_CreatePiAnchorConstruct(int argc, VALUE *argv, VALUE self)
9521 Data_Get_Struct(self, Molecule, mol);
9522 rb_scan_args(argc, argv, "22", vals, vals + 1, vals + 2, vals + 3);
9523 for (i = 0; i < 4; i++) {
9524 if (vals[i] == Qnil)
9526 else ivals[i] = s_Molecule_AtomOrPiAtomIndexFromValue(mol, vals[i]);
9528 for (i = 0; i < 4; i++) {
9529 if (ivals[i] >= ATOMS_MAX_NUMBER)
9533 rb_raise(rb_eMolbyError, "No pi anchor is specified");
9534 ig = IntGroupNewWithPoints(mol->npibonds, 1, -1);
9535 MolActionCreateAndPerform(mol, gMolActionInsertPiBonds, ig, 4, ivals);
9536 IntGroupRelease(ig);
9537 return INT2NUM(mol->npibonds);
9542 * remove_pi_anchor_constructs(IntGroup) -> self
9544 * Remove pi anchor constructs (bond, angle, dihedral) with indices specified in IntGroup.
9545 * This operation is undoable.
9548 s_Molecule_RemovePiAnchorConstructs(VALUE self, VALUE igval)
9552 Data_Get_Struct(self, Molecule, mol);
9553 ig = IntGroupFromValue(igval);
9554 MolActionCreateAndPerform(mol, gMolActionRemovePiBonds, ig);
9555 IntGroupRelease(ig);
9561 * count_pi_anchor_constructs -> Integer
9563 * Returns the number of pi anchor constructs.
9566 s_Molecule_CountPiAnchorConstructs(VALUE self)
9569 Data_Get_Struct(self, Molecule, mol);
9570 return INT2NUM(mol->npibonds);
9575 * pi_anchor_construct(index) -> Array of Integers
9577 * Returns the elements representing the index-th pi anchor constructs.
9580 s_Molecule_PiAnchorConstructAtIndex(VALUE self, VALUE ival)
9585 Data_Get_Struct(self, Molecule, mol);
9586 idx = NUM2INT(rb_Integer(ival));
9587 if (idx < 0 || idx >= mol->npibonds)
9588 rb_raise(rb_eMolbyError, "index of pi anchor constructs out of range (%d)", idx);
9589 for (i = 0; i < 4; i++) {
9590 n = mol->pibonds[idx * 4 + i];
9593 if (n >= ATOMS_MAX_NUMBER)
9594 n = -(n - ATOMS_MAX_NUMBER) - 1;
9595 vals[i] = INT2NUM(n);
9597 return rb_ary_new4(i, vals);
9602 * current -> Molecule
9604 * Get the currently "active" molecule.
9607 s_Molecule_Current(VALUE klass)
9609 return ValueFromMolecule(MoleculeCallback_currentMolecule());
9614 * Molecule[] -> Molecule
9615 * Molecule[n] -> Molecule
9616 * Molecule[name] -> Molecule
9617 * Molecule[name, k] -> Molecule
9618 * Molecule[regex] -> Molecule
9619 * Molecule[regex, k] -> Molecule
9621 * Molecule[] is equivalent to Molecule.current.
9622 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
9623 * Molecule[name] gives the first document (in the order of creation time) that has
9624 * the given name. If a second argument (k) is given, the k-th document that has the
9625 * given name is returned.
9626 * Molecule[regex] gives the first document (in the order of creation time) that
9627 * has a name matching the regular expression. If a second argument (k) is given,
9628 * the k-th document that has a name matching the re is returned.
9631 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
9637 rb_scan_args(argc, argv, "02", &val, &kval);
9639 return s_Molecule_Current(klass);
9640 if (rb_obj_is_kind_of(val, rb_cInteger)) {
9642 mol = MoleculeCallback_moleculeAtIndex(idx);
9643 } else if (rb_obj_is_kind_of(val, rb_cString)) {
9644 char *p = StringValuePtr(val);
9645 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9646 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9647 MoleculeCallback_displayName(mol, buf, sizeof buf);
9648 if (strcmp(buf, p) == 0 && --k == 0)
9651 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
9652 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9653 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9655 MoleculeCallback_displayName(mol, buf, sizeof buf);
9656 name = rb_str_new2(buf);
9657 if (rb_reg_match(val, name) != Qnil && --k == 0)
9660 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
9664 else return ValueFromMolecule(mol);
9669 * list -> array of Molecules
9671 * Get the list of molecules associated to the documents, in the order of creation
9672 * time of the document. If no document is open, returns an empry array.
9675 s_Molecule_List(VALUE klass)
9682 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
9683 rb_ary_push(ary, ValueFromMolecule(mol));
9691 * ordered_list -> array of Molecules
9693 * Get the list of molecules associated to the documents, in the order of front-to-back
9694 * ordering of the associated window. If no document is open, returns an empry array.
9697 s_Molecule_OrderedList(VALUE klass)
9704 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
9705 rb_ary_push(ary, ValueFromMolecule(mol));
9716 /* Define module Molby */
9717 rb_mMolby = rb_define_module("Molby");
9719 /* Define Vector3D, Transform, IntGroup */
9722 /* Define MDArena */
9723 Init_MolbyMDTypes();
9725 /* class Molecule */
9726 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
9727 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
9728 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
9729 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
9730 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
9731 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
9732 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
9733 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
9734 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
9735 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
9736 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
9737 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
9738 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
9739 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
9740 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
9741 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
9742 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
9743 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
9744 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
9745 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
9746 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
9747 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
9748 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
9749 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
9750 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
9751 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
9752 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
9753 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
9754 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
9755 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
9756 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
9757 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
9758 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
9759 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
9760 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
9761 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
9762 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
9763 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
9765 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondPar, 1);
9766 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_AnglePar, 1);
9767 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_DihedralPar, 1);
9768 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_ImproperPar, 1);
9769 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_VdwPar, 1);
9771 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
9772 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
9773 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
9774 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
9775 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
9776 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
9778 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
9779 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
9780 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
9781 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
9782 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
9783 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
9784 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
9785 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
9786 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
9787 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
9788 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
9789 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
9790 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
9791 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
9792 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
9793 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
9794 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
9795 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
9796 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
9797 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
9798 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
9799 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
9800 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
9801 rb_define_alias(rb_cMolecule, "+", "add");
9802 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
9803 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
9804 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
9805 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
9806 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
9807 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
9808 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
9809 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
9810 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
9811 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
9812 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
9813 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
9814 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
9815 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
9816 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, 4);
9817 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
9818 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
9819 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
9820 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
9821 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
9822 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
9823 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
9824 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
9825 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
9826 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
9827 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
9828 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
9829 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
9830 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
9831 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
9832 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
9833 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
9834 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
9835 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
9836 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
9837 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
9838 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
9839 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
9840 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
9841 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
9842 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
9843 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
9844 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
9845 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
9846 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, 0);
9847 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
9848 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
9849 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
9850 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
9851 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
9852 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
9853 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
9854 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
9855 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
9856 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
9857 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
9858 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
9859 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
9860 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
9861 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
9862 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
9863 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
9864 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
9865 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
9866 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
9867 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
9868 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
9869 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
9870 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
9871 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
9872 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
9873 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
9874 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
9875 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
9876 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
9877 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
9878 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
9879 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
9880 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
9881 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
9882 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
9883 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
9884 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
9885 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
9886 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
9887 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
9888 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
9889 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
9890 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
9891 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
9892 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
9893 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
9894 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
9895 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
9896 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
9897 #if !defined(__CMDMAC__)
9898 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
9899 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
9900 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
9901 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
9902 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
9903 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
9904 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
9905 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
9906 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
9907 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
9908 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
9909 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
9910 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
9911 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
9913 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
9914 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
9915 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
9916 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
9917 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
9918 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
9919 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
9920 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
9921 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
9922 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
9923 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
9924 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
9925 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
9926 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
9927 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
9929 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
9930 rb_define_method(rb_cMolecule, "replace_pi_anchor", s_Molecule_ReplacePiAnchor, -1);
9931 rb_define_method(rb_cMolecule, "insert_pi_anchor", s_Molecule_InsertPiAnchor, -1);
9932 rb_define_method(rb_cMolecule, "remove_pi_anchor", s_Molecule_RemovePiAnchor, 1);
9933 rb_define_method(rb_cMolecule, "pi_anchor", s_Molecule_PiAnchorAtIndex, 1);
9934 rb_define_method(rb_cMolecule, "count_pi_anchors", s_Molecule_CountPiAnchors, 0);
9935 rb_define_method(rb_cMolecule, "create_pi_anchor_construct", s_Molecule_CreatePiAnchorConstruct, -1);
9936 rb_define_method(rb_cMolecule, "remove_pi_anchor_constructs", s_Molecule_RemovePiAnchorConstructs, 1);
9937 rb_define_alias(rb_cMolecule, "remove_pi_anchor_construct", "remove_pi_anchor_constructs");
9938 rb_define_method(rb_cMolecule, "count_pi_anchor_constructs", s_Molecule_CountPiAnchorConstructs, 0);
9939 rb_define_method(rb_cMolecule, "pi_anchor_construct", s_Molecule_PiAnchorConstructAtIndex, 1);
9941 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
9942 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
9943 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
9944 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
9945 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
9947 /* class MolEnumerable */
9948 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
9949 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
9950 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
9951 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
9952 rb_define_alias(rb_cMolEnumerable, "size", "length");
9953 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
9956 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
9957 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
9959 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
9960 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
9961 s_AtomAttrDefTable[i].id = rb_intern(buf);
9962 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
9964 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
9966 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
9967 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
9968 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
9969 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
9970 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
9971 rb_global_variable(&s_SetAtomAttrString);
9972 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
9974 /* class Parameter */
9975 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
9976 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
9977 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
9978 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
9979 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
9980 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
9981 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
9982 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
9983 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
9984 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
9985 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
9986 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
9987 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
9988 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
9989 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
9990 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
9991 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
9992 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
9993 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
9994 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
9995 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
9996 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
9997 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
9998 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
9999 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10000 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10001 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10002 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10004 /* class ParEnumerable */
10005 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10006 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10007 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10008 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10009 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10010 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10011 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10012 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10013 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10014 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10016 /* class ParameterRef */
10017 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10018 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10020 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10021 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10022 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10023 if (s_ParameterAttrDefTable[i].symref != NULL)
10024 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10025 if (s_ParameterAttrDefTable[i].setter != NULL) {
10027 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10030 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10031 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10032 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10033 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10034 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10035 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10036 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10038 /* class MolbyError */
10039 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10041 /* module Kernel */
10042 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10043 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10044 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10045 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10046 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10047 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10048 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10049 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10050 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10051 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10052 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10053 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10054 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10055 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10056 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, 2);
10057 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10058 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10060 s_ID_equal = rb_intern("==");
10063 #pragma mark ====== External functions ======
10065 static VALUE s_ruby_top_self = Qfalse;
10068 s_evalRubyScriptOnMoleculeSub(VALUE val)
10070 void **ptr = (void **)val;
10071 Molecule *mol = (Molecule *)ptr[1];
10072 VALUE sval = rb_str_new2((char *)ptr[0]);
10074 if (s_ruby_top_self == Qfalse) {
10075 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10077 if (ptr[2] == NULL) {
10080 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10084 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
10086 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
10088 VALUE mval = ValueFromMolecule(mol);
10090 return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
10091 else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
10096 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10100 VALUE save_interrupt_flag;
10101 char *save_ruby_sourcefile;
10102 int save_ruby_sourceline;
10103 if (gMolbyIsCheckingInterrupt) {
10104 MolActionAlertRubyIsRunning();
10106 return (RubyValue)Qnil;
10109 args[0] = (void *)script;
10110 args[1] = (void *)mol;
10111 args[2] = (void *)fname;
10112 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10113 save_ruby_sourcefile = ruby_sourcefile;
10114 save_ruby_sourceline = ruby_sourceline;
10115 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10116 s_SetInterruptFlag(Qnil, save_interrupt_flag);
10117 ruby_sourcefile = save_ruby_sourcefile;
10118 ruby_sourceline = save_ruby_sourceline;
10124 Molby_showRubyValue(RubyValue value)
10126 VALUE val = (VALUE)value;
10127 if (gMolbyIsCheckingInterrupt) {
10128 MolActionAlertRubyIsRunning();
10134 val = rb_protect(rb_inspect, val, &status);
10136 MyAppCallback_showScriptMessage("%s", StringValuePtr(val));
10141 Molby_showError(int status)
10143 static const int tag_raise = 6;
10144 char *msg = NULL, *msg2;
10145 VALUE val, backtrace;
10146 int interrupted = 0;
10147 if (status == tag_raise) {
10148 VALUE eclass = CLASS_OF(ruby_errinfo);
10149 if (eclass == rb_eInterrupt) {
10155 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10157 val = rb_eval_string_protect("$!.to_s", &status);
10159 msg = RSTRING_PTR(val);
10160 else msg = "(message not available)";
10162 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10163 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10169 Molby_startup(const char *script, const char *dir)
10176 /* Initialize Ruby interpreter */
10179 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
10181 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
10182 ruby_incpush(libpath);
10186 ruby_script("Molby");
10188 /* Find the resource path (the parent directory of the given directory) */
10189 respath = strdup(dir);
10190 p = strrchr(respath, '/');
10191 if (p == NULL && PATH_SEPARATOR != '/')
10192 p = strrchr(respath, PATH_SEPARATOR);
10195 val = Ruby_NewFileStringValue(respath);
10196 rb_define_global_const("MolbyResourcePath", val);
10199 /* Define Molby classes */
10201 RubyDialogInitClass();
10203 rb_define_const(rb_mMolby, "ResourcePath", val);
10204 val = Ruby_NewFileStringValue(dir);
10205 rb_define_const(rb_mMolby, "ScriptPath", val);
10206 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
10207 val = Ruby_NewFileStringValue(p);
10208 rb_define_const(rb_mMolby, "MbsfPath", val);
10213 /* Create objects for stdout and stderr */
10214 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10215 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
10216 rb_gv_set("$stdout", val);
10217 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10218 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
10219 rb_gv_set("$stderr", val);
10221 /* Create objects for stdin */
10222 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10223 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
10224 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
10225 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
10226 rb_gv_set("$stdin", val);
10230 /* Global variable to hold backtrace */
10231 rb_define_variable("$backtrace", &gMolbyBacktrace);
10234 /* Register interrupt check code */
10235 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
10238 /* Get version/copyright string from Ruby interpreter */
10240 gRubyVersion = strdup(ruby_version);
10241 asprintf(&gRubyCopyright, " Copyright (C) %d-%d %s",
10242 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
10246 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
10247 s_SetIntervalTimer(0, 50);
10250 /* Read the startup script */
10251 if (script != NULL && script[0] != 0) {
10252 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
10254 rb_load_protect(rb_str_new2(script), 0, &status);
10257 Molby_showError(status);
10259 MyAppCallback_showScriptMessage("Done.\n");