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);
649 if (retval == (VALUE)6 && status == -1)
650 rb_raise(rb_eMolbyError, "Cannot open script file: %s", StringValuePtr(fname));
660 * Get the directory suitable for storing user documents. On Windows
661 * it is the home directory + "My Documents". On other platforms
662 * it is the home directory.
665 s_Kernel_DocumentHome(VALUE self)
667 char *s = MyAppCallback_getDocumentHomeDir();
668 VALUE retval = Ruby_NewFileStringValue(s);
675 * call_subprocess(cmd, process_name)
677 * Call subprocess. A progress dialog window is displayed, with a message
678 * "Running #{process_name}...".
681 s_Kernel_CallSubProcess(VALUE self, VALUE cmd, VALUE procname)
683 int n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname));
689 * enable_filter_mode -> Integer
691 * Switch to a filter mode. Should be invoked from within a script file.
692 * Returns true when successfully switched, false when already in filter mode.
693 * No Molecule should be open as a view; otherwise, an exception is raised.
696 s_Kernel_EnableFilterMode(VALUE self)
698 int n = MyAppCallback_switchToFilterMode();
704 rb_raise(rb_eMolbyError, "To switch to filter mode, all molecule should be closed");
706 rb_raise(rb_eMolbyError, "No script file is specified for filter mode");
708 rb_raise(rb_eMolbyError, "Cannot switch to filter mode");
709 return Qnil; /* Dummy to keep compiler happy */
712 #pragma mark ====== User defaults ======
716 * get_global_settings(key)
718 * Get a setting data for key from the application preferences.
721 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
723 char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
725 VALUE retval = rb_eval_string(p);
733 * set_global_settings(key, value)
735 * Set a setting data for key to the application preferences.
738 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
740 VALUE sval = rb_inspect(value);
741 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(sval));
745 #pragma mark ====== Utility functions (protected funcall) ======
747 struct Ruby_funcall2_record {
755 s_Ruby_funcall2_sub(VALUE data)
757 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
758 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
762 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
764 struct Ruby_funcall2_record rec;
769 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
772 #pragma mark ====== ParameterRef Class ======
775 s_UnionParFromValue(VALUE self, Int *typep, Int checkEditable)
779 Data_Get_Struct(self, ParameterRef, pref);
781 *typep = pref->parType;
782 if (pref->parType == kElementParType) {
783 up = (UnionPar *)&gElementParameters[pref->idx];
785 up = ParameterRefGetPar(pref);
788 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is internally cached in the MDArena");
789 if (up->bond.src != 0 && up->bond.src != -1)
790 rb_raise(rb_eMolbyError, "Cannot modify parameter because it is not molecule-local");
797 s_RegisterUndoForParameterAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval, int oldsrc)
801 Data_Get_Struct(self, ParameterRef, pref);
802 if (pref->mol == NULL)
804 up = ParameterRefGetPar(pref);
805 if (key != s_SourceSym)
806 up->bond.src = 0; /* Becomes automatically molecule-local */
807 if (MolActionCallback_isUndoRegistrationEnabled(pref->mol)) {
810 act = MolActionNew(SCRIPT_ACTION("iirri"), "set_parameter_attr", pref->parType, pref->idx, key, oldval, oldsrc);
811 MolActionCallback_registerUndo(pref->mol, act);
812 MoleculeCallback_notifyModification(pref->mol, 0);
813 pref->mol->needsMDRebuild = 1;
818 ValueFromMoleculeWithParameterTypeAndIndex(Molecule *mol, int type, int idx1)
820 ParameterRef *pref = ParameterRefNew(mol, type, idx1);
822 return Data_Wrap_Struct(rb_cParameterRef, 0, (void (*)(void *))ParameterRefRelease, pref);
824 rb_raise(rb_eMolbyError, "Cannot create parameter reference");
828 s_AtomTypeIndexFromValue(VALUE val)
830 if (rb_obj_is_kind_of(val, rb_cNumeric))
833 return AtomTypeEncodeToUInt(StringValuePtr(val));
836 static const char *s_ParameterTypeNames[] = {
837 "bond", "angle", "dihedral", "improper", "vdw", "vdw_pair", "vdw_cutoff", "element"
839 static ID s_ParameterTypeIDs[8] = {0, 0, 0, 0, 0, 0, 0, 0};
842 s_ParTypeFromValue(VALUE val)
846 n = sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]);
847 if (s_ParameterTypeIDs[0] == 0) {
848 for (i = 0; i < n; i++)
849 s_ParameterTypeIDs[i] = rb_intern(s_ParameterTypeNames[i]);
851 valid = rb_to_id(val);
852 for (i = 0; i < n; i++) {
853 if (valid == s_ParameterTypeIDs[i]) {
855 return kElementParType;
856 else return kFirstParType + i;
859 return kInvalidParType;
866 * Get the index in the parameter list.
868 static VALUE s_ParameterRef_GetIndex(VALUE self) {
870 Data_Get_Struct(self, ParameterRef, pref);
871 return INT2NUM(pref->idx);
878 * Get the parameter type, like "bond", "angle", etc.
880 static VALUE s_ParameterRef_GetParType(VALUE self) {
882 s_UnionParFromValue(self, &tp, 0);
883 if (tp == kElementParType)
884 return rb_str_new2("element");
886 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
887 return rb_str_new2(s_ParameterTypeNames[tp]);
888 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
893 * atom_type -> String or Array of String
894 * atom_types -> String or Array of String
896 * Get the atom types. For a bond parameter, an array of two strings (like ["ca", "ha"])
897 * is returned. For an angle parameter, an array of three strings (like ["ha", "ca", "ha"])
898 * is returned. For a dihedral or improper parameter, an array of four strings is returned.
899 * The atom type may be "X", which is a wildcard that matches any atom type.
901 static VALUE s_ParameterRef_GetAtomTypes(VALUE self) {
906 up = s_UnionParFromValue(self, &tp, 0);
907 n = ParameterGetAtomTypes(tp, up, types);
909 rb_raise(rb_eMolbyError, "invalid member atom_types");
910 for (i = 0; i < n; i++) {
911 if (types[i] >= 0 && types[i] < kAtomTypeMinimum)
912 vals[i] = INT2NUM(types[i]);
914 vals[i] = rb_str_new2(AtomTypeDecodeToString(types[i], NULL));
919 return rb_ary_new4(n, vals);
926 * Get the force constant. Available for bond, angle, dihedral, and improper parameters.
928 static VALUE s_ParameterRef_GetK(VALUE self) {
932 up = s_UnionParFromValue(self, &tp, 0);
935 return rb_float_new(up->bond.k * INTERNAL2KCAL);
937 return rb_float_new(up->angle.k * INTERNAL2KCAL);
938 case kDihedralParType:
939 case kImproperParType:
940 if (up->torsion.mult == 1)
941 return rb_float_new(up->torsion.k[0] * INTERNAL2KCAL);
942 n = up->torsion.mult;
945 for (i = 0; i < n; i++)
946 vals[i] = rb_float_new(up->torsion.k[i] * INTERNAL2KCAL);
947 return rb_ary_new4(n, vals);
949 rb_raise(rb_eMolbyError, "invalid member k");
957 * Get the equilibrium bond length. Only available for bond parameters.
959 static VALUE s_ParameterRef_GetR0(VALUE self) {
962 up = s_UnionParFromValue(self, &tp, 0);
963 if (tp == kBondParType)
964 return rb_float_new(up->bond.r0);
965 else rb_raise(rb_eMolbyError, "invalid member r0");
972 * Get the equilibrium angle (in degree). Only available for angle parameters.
974 static VALUE s_ParameterRef_GetA0(VALUE self) {
977 up = s_UnionParFromValue(self, &tp, 0);
978 if (tp == kAngleParType)
979 return rb_float_new(up->angle.a0 * kRad2Deg);
980 else rb_raise(rb_eMolbyError, "invalid member a0");
987 * Get the multiplicity. Only available for dihedral and improper parameters.
988 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
990 static VALUE s_ParameterRef_GetMult(VALUE self) {
993 up = s_UnionParFromValue(self, &tp, 0);
994 if (tp == kDihedralParType || tp == kImproperParType)
995 return rb_float_new(up->torsion.mult);
996 else rb_raise(rb_eMolbyError, "invalid member mult");
1001 * period -> Integer or Array of Integers
1003 * Get the periodicity. Only available for dihedral and improper parameters.
1004 * If the multiplicity is larger than 1, then an array of integers is returned.
1005 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1007 static VALUE s_ParameterRef_GetPeriod(VALUE self) {
1011 up = s_UnionParFromValue(self, &tp, 0);
1012 if (tp == kDihedralParType || tp == kImproperParType) {
1013 if (up->torsion.mult == 1)
1014 return INT2NUM(up->torsion.period[0]);
1015 n = up->torsion.mult;
1018 for (i = 0; i < n; i++)
1019 vals[i] = INT2NUM(up->torsion.period[i]);
1020 return rb_ary_new4(n, vals);
1021 } else rb_raise(rb_eMolbyError, "invalid member period");
1026 * phi0 -> Float or Array of Floats
1028 * Get the equilibrium dihedral angle. Only available for dihedral and improper parameters.
1029 * If the multiplicity is larger than 1, then an array of floats is returned.
1030 * (Note: Implementation of multiple dihedral/improper parameters is not well tested)
1032 static VALUE s_ParameterRef_GetPhi0(VALUE self) {
1036 up = s_UnionParFromValue(self, &tp, 0);
1037 if (tp == kDihedralParType || tp == kImproperParType) {
1038 if (up->torsion.mult == 1)
1039 return rb_float_new(up->torsion.phi0[0] * kRad2Deg);
1040 n = up->torsion.mult;
1043 for (i = 0; i < n; i++)
1044 vals[i] = rb_float_new(up->torsion.phi0[i] * kRad2Deg);
1045 return rb_ary_new4(n, vals);
1046 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1053 * Get the "A" value for the van der Waals parameter.
1056 static VALUE s_ParameterRef_GetA(VALUE self) {
1059 up = s_UnionParFromValue(self, &tp, 0);
1060 if (tp == kVdwParType)
1061 return rb_float_new(up->vdw.A);
1062 else if (tp == kVdwPairParType)
1063 return rb_float_new(up->vdwp.A);
1064 else rb_raise(rb_eMolbyError, "invalid member A");
1072 * Get the "B" value for the van der Waals parameter.
1075 static VALUE s_ParameterRef_GetB(VALUE self) {
1078 up = s_UnionParFromValue(self, &tp, 0);
1079 if (tp == kVdwParType)
1080 return rb_float_new(up->vdw.B);
1081 else if (tp == kVdwPairParType)
1082 return rb_float_new(up->vdwp.B);
1083 else rb_raise(rb_eMolbyError, "invalid member B");
1091 * Get the equilibrium radius (half of the minimum energy distance) for the van der Waals parameter.
1093 static VALUE s_ParameterRef_GetReq(VALUE self) {
1096 /* Double a, b, r; */
1098 up = s_UnionParFromValue(self, &tp, 0);
1099 if (tp == kVdwParType) {
1103 } else if (tp == kVdwPairParType) {
1107 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1108 /* if (a == 0.0 || b == 0.0) */
1109 return rb_float_new(r);
1110 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1117 * Get the minimum energy for the van der Waals parameter.
1119 static VALUE s_ParameterRef_GetEps(VALUE self) {
1124 up = s_UnionParFromValue(self, &tp, 0);
1125 if (tp == kVdwParType) {
1129 } else if (tp == kVdwPairParType) {
1133 } else rb_raise(rb_eMolbyError, "invalid member eps");
1134 /* if (a == 0.0 || b == 0.0) */
1135 return rb_float_new(eps * INTERNAL2KCAL);
1136 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1143 * Get the "A" value for the 1-4 van der Waals parameter.
1146 static VALUE s_ParameterRef_GetA14(VALUE self) {
1149 up = s_UnionParFromValue(self, &tp, 0);
1150 if (tp == kVdwParType)
1151 return rb_float_new(up->vdw.A14);
1152 else if (tp == kVdwPairParType)
1153 return rb_float_new(up->vdwp.A14);
1154 else rb_raise(rb_eMolbyError, "invalid member A14");
1162 * Get the "B" value for the 1-4 van der Waals parameter.
1165 static VALUE s_ParameterRef_GetB14(VALUE self) {
1168 up = s_UnionParFromValue(self, &tp, 0);
1169 if (tp == kVdwParType)
1170 return rb_float_new(up->vdw.B14);
1171 else if (tp == kVdwPairParType)
1172 return rb_float_new(up->vdwp.B14);
1173 else rb_raise(rb_eMolbyError, "invalid member B14");
1181 * Get the equilibrium radius (half of the minimum energy distance) for the 1-4 van der Waals parameter.
1183 static VALUE s_ParameterRef_GetReq14(VALUE self) {
1186 /* Double a, b, r; */
1188 up = s_UnionParFromValue(self, &tp, 0);
1189 if (tp == kVdwParType) {
1193 } else if (tp == kVdwPairParType) {
1194 /* a = up->vdwp.A14;
1195 b = up->vdwp.B14; */
1196 r = up->vdwp.r_eq14;
1197 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1198 /* if (a == 0.0 || b == 0.0) */
1199 return rb_float_new(r);
1200 /* else return rb_float_new(pow(2*a/b, 1.0/6.0)); */
1207 * Get the minimum energy for the 1-4 van der Waals parameter.
1209 static VALUE s_ParameterRef_GetEps14(VALUE self) {
1214 up = s_UnionParFromValue(self, &tp, 0);
1215 if (tp == kVdwParType) {
1219 } else if (tp == kVdwPairParType) {
1220 /* a = up->vdwp.A14;
1221 b = up->vdwp.B14; */
1223 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1224 /* if (a == 0.0 || b == 0.0) */
1225 return rb_float_new(eps * INTERNAL2KCAL);
1226 /* else return rb_float_new(b*b/a/4.0 * INTERNAL2KCAL); */
1233 * Get the cutoff distance for the van der Waals pair-specific cutoff parameter.
1235 static VALUE s_ParameterRef_GetCutoff(VALUE self) {
1238 up = s_UnionParFromValue(self, &tp, 0);
1239 if (tp == kVdwCutoffParType)
1240 return rb_float_new(up->vdwcutoff.cutoff);
1241 else rb_raise(rb_eMolbyError, "invalid member cutoff");
1248 * Get the atomic radius for the atom display parameter.
1250 static VALUE s_ParameterRef_GetRadius(VALUE self) {
1253 up = s_UnionParFromValue(self, &tp, 0);
1254 if (tp == kElementParType)
1255 return rb_float_new(up->atom.radius);
1256 else rb_raise(rb_eMolbyError, "invalid member radius");
1261 * color -> [Float, Float, Float]
1263 * Get the rgb color for the atom display parameter.
1265 static VALUE s_ParameterRef_GetColor(VALUE self) {
1268 up = s_UnionParFromValue(self, &tp, 0);
1269 if (tp == kElementParType)
1270 return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
1271 else rb_raise(rb_eMolbyError, "invalid member color");
1276 * atomic_number -> Integer
1278 * Get the atomic number for the vdw or atom parameter.
1280 static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
1283 up = s_UnionParFromValue(self, &tp, 0);
1284 if (tp == kElementParType)
1285 return INT2NUM(up->atom.number);
1286 else if (tp == kVdwParType)
1287 return INT2NUM(up->vdw.atomicNumber);
1288 else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1295 * Get the name for the atom display parameter.
1297 static VALUE s_ParameterRef_GetName(VALUE self) {
1300 up = s_UnionParFromValue(self, &tp, 0);
1301 if (tp == kElementParType) {
1303 strncpy(name, up->atom.name, 4);
1305 return rb_str_new2(name);
1306 } else rb_raise(rb_eMolbyError, "invalid member name");
1313 * Get the atomic weight for the atom display parameter.
1315 static VALUE s_ParameterRef_GetWeight(VALUE self) {
1318 up = s_UnionParFromValue(self, &tp, 0);
1319 if (tp == kElementParType)
1320 return rb_float_new(up->atom.weight);
1321 else if (tp == kVdwParType)
1322 return rb_float_new(up->vdw.weight);
1323 else rb_raise(rb_eMolbyError, "invalid member weight");
1328 * fullname -> String
1330 * Get the full name for the atom display parameter.
1332 static VALUE s_ParameterRef_GetFullName(VALUE self) {
1335 up = s_UnionParFromValue(self, &tp, 0);
1336 if (tp == kElementParType) {
1338 strncpy(fullname, up->atom.fullname, 15);
1340 return rb_str_new2(fullname);
1341 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1348 * Get the comment for the parameter.
1350 static VALUE s_ParameterRef_GetComment(VALUE self) {
1353 up = s_UnionParFromValue(self, &tp, 0);
1357 else return rb_str_new2(ParameterGetComment(com));
1364 * Get the source string for the parameter. Returns false for undefined parameter,
1365 * and nil for "local" parameter that is specific for the molecule.
1367 static VALUE s_ParameterRef_GetSource(VALUE self) {
1370 up = s_UnionParFromValue(self, &tp, 0);
1373 return Qfalse; /* undefined */
1375 return Qnil; /* local */
1376 else return rb_str_new2(ParameterGetComment(src));
1380 s_ScanAtomTypes(VALUE val, Int n, UInt *types)
1387 if (rb_obj_is_kind_of(val, rb_cString)) {
1388 char *s = StringValuePtr(val);
1390 for (i = 0; i < n; i++) {
1393 /* Skip leading separaters */
1394 while (*s == '-' || *s == ' ' || *s == '\t')
1396 for (p = s; *p != 0; p++) {
1397 if (*p == '-' || *p == ' ' || *p == '\t')
1401 if (len >= sizeof(buf))
1402 len = sizeof(buf) - 1;
1403 strncpy(buf, s, len);
1405 /* Skip trailing blanks */
1406 while (--len >= 0 && (buf[len] == ' ' || buf[len] == '\t'))
1409 rb_raise(rb_eMolbyError, "Bad atom type specification: %s", StringValuePtr(val));
1410 if (buf[0] >= '0' && buf[0] <= '9')
1411 types[i] = atoi(buf);
1413 types[i] = AtomTypeEncodeToUInt(buf);
1414 if (p == NULL || *p == 0) {
1420 rb_raise(rb_eMolbyError, "%d atom types are required but only %d are given; %s", n, i, StringValuePtr(val));
1423 val = rb_ary_to_ary(val);
1424 if (RARRAY_LEN(val) != n)
1425 rb_raise(rb_eMolbyError, "an array of %d atom types is required", n);
1426 valp = RARRAY_PTR(val);
1428 for (i = 0; i < n; i++) {
1429 if (rb_obj_is_kind_of(valp[i], rb_cNumeric))
1430 types[i] = NUM2INT(rb_Integer(valp[i]));
1432 VALUE sval = valp[i];
1433 types[i] = AtomTypeEncodeToUInt(StringValuePtr(sval));
1438 static VALUE s_ParameterRef_SetAtomTypes(VALUE self, VALUE val) {
1443 up = s_UnionParFromValue(self, &tp, 1);
1444 oldval = s_ParameterRef_GetAtomTypes(self);
1445 oldsrc = up->bond.src;
1448 s_ScanAtomTypes(val, 2, types);
1449 up->bond.type1 = types[0];
1450 up->bond.type2 = types[1];
1453 s_ScanAtomTypes(val, 3, types);
1454 up->angle.type1 = types[0];
1455 up->angle.type2 = types[1];
1456 up->angle.type3 = types[2];
1458 case kDihedralParType:
1459 case kImproperParType:
1460 s_ScanAtomTypes(val, 4, types);
1461 up->torsion.type1 = types[0];
1462 up->torsion.type2 = types[1];
1463 up->torsion.type3 = types[2];
1464 up->torsion.type4 = types[3];
1467 s_ScanAtomTypes(val, 1, types);
1468 up->vdw.type1 = types[0];
1470 case kVdwPairParType:
1471 s_ScanAtomTypes(val, 2, types);
1472 up->vdwp.type1 = types[0];
1473 up->vdwp.type2 = types[1];
1475 case kVdwCutoffParType:
1476 s_ScanAtomTypes(val, 2, types);
1477 up->vdwcutoff.type1 = types[0];
1478 up->vdwcutoff.type2 = types[1];
1483 s_RegisterUndoForParameterAttrChange(self, s_AtomTypesSym, val, oldval, oldsrc);
1487 static VALUE s_ParameterRef_SetK(VALUE self, VALUE val) {
1489 Int tp, i, n, oldsrc;
1490 VALUE *valp, oldval;
1491 up = s_UnionParFromValue(self, &tp, 1);
1492 oldval = s_ParameterRef_GetK(self);
1493 oldsrc = up->bond.src;
1496 val = rb_Float(val);
1497 up->bond.k = NUM2DBL(val) * KCAL2INTERNAL;
1500 val = rb_Float(val);
1501 up->angle.k = NUM2DBL(val) * KCAL2INTERNAL;
1503 case kDihedralParType:
1504 case kImproperParType:
1505 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1506 up->torsion.mult = 1;
1507 val = rb_Float(val);
1508 up->torsion.k[0] = NUM2DBL(val) * KCAL2INTERNAL;
1511 n = up->torsion.mult;
1514 val = rb_ary_to_ary(val);
1515 if (RARRAY_LEN(val) != n)
1516 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1517 valp = RARRAY_PTR(val);
1518 for (i = 0; i < n; i++) {
1519 up->torsion.k[i] = NUM2DBL(rb_Float(valp[i])) * KCAL2INTERNAL;
1523 rb_raise(rb_eMolbyError, "invalid member k");
1525 s_RegisterUndoForParameterAttrChange(self, s_KSym, val, oldval, oldsrc);
1529 static VALUE s_ParameterRef_SetR0(VALUE self, VALUE val) {
1533 up = s_UnionParFromValue(self, &tp, 1);
1534 oldval = s_ParameterRef_GetR0(self);
1535 oldsrc = up->bond.src;
1536 if (tp == kBondParType) {
1537 val = rb_Float(val);
1538 up->bond.r0 = NUM2DBL(val);
1539 } else rb_raise(rb_eMolbyError, "invalid member r0");
1540 s_RegisterUndoForParameterAttrChange(self, s_R0Sym, val, oldval, oldsrc);
1544 static VALUE s_ParameterRef_SetA0(VALUE self, VALUE val) {
1548 up = s_UnionParFromValue(self, &tp, 1);
1549 oldval = s_ParameterRef_GetA0(self);
1550 oldsrc = up->bond.src;
1551 if (tp == kAngleParType) {
1552 val = rb_Float(val);
1553 up->angle.a0 = NUM2DBL(val) * kDeg2Rad;
1554 } else rb_raise(rb_eMolbyError, "invalid member a0");
1555 s_RegisterUndoForParameterAttrChange(self, s_A0Sym, val, oldval, oldsrc);
1559 static VALUE s_ParameterRef_SetMult(VALUE self, VALUE val) {
1563 up = s_UnionParFromValue(self, &tp, 1);
1564 oldval = s_ParameterRef_GetMult(self);
1565 oldsrc = up->bond.src;
1566 if (tp == kDihedralParType || tp == kImproperParType) {
1568 val = rb_Integer(val);
1571 rb_raise(rb_eMolbyError, "torsion multiplicity should be 0..3");
1572 up->torsion.mult = i;
1573 } else rb_raise(rb_eMolbyError, "invalid member mult");
1574 s_RegisterUndoForParameterAttrChange(self, s_MultSym, val, oldval, oldsrc);
1578 static VALUE s_ParameterRef_SetPeriod(VALUE self, VALUE val) {
1580 Int tp, i, n, oldsrc;
1581 VALUE *valp, oldval;
1582 up = s_UnionParFromValue(self, &tp, 1);
1583 oldval = s_ParameterRef_GetPeriod(self);
1584 oldsrc = up->bond.src;
1585 if (tp == kDihedralParType || tp == kImproperParType) {
1586 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1587 up->torsion.mult = 1;
1588 val = rb_Integer(val);
1589 up->torsion.period[0] = NUM2INT(val);
1591 n = up->torsion.mult;
1594 val = rb_ary_to_ary(val);
1595 if (RARRAY_LEN(val) != n)
1596 rb_raise(rb_eMolbyError, "the value should be an array of %d integers", n);
1597 valp = RARRAY_PTR(val);
1598 for (i = 0; i < n; i++) {
1599 up->torsion.period[i] = NUM2INT(rb_Integer(valp[i]));
1602 } else rb_raise(rb_eMolbyError, "invalid member period");
1603 s_RegisterUndoForParameterAttrChange(self, s_PeriodSym, val, oldval, oldsrc);
1607 static VALUE s_ParameterRef_SetPhi0(VALUE self, VALUE val) {
1609 Int tp, i, n, oldsrc;
1610 VALUE *valp, oldval;
1611 up = s_UnionParFromValue(self, &tp, 1);
1612 oldval = s_ParameterRef_GetPhi0(self);
1613 oldsrc = up->bond.src;
1614 if (tp == kDihedralParType || tp == kImproperParType) {
1615 if (up->torsion.mult == 1 || up->torsion.mult == 0) {
1616 up->torsion.mult = 1;
1617 val = rb_Float(val);
1618 up->torsion.phi0[0] = NUM2DBL(val) * kDeg2Rad;
1620 n = up->torsion.mult;
1623 val = rb_ary_to_ary(val);
1624 if (RARRAY_LEN(val) != n)
1625 rb_raise(rb_eMolbyError, "the value should be an array of %d floats", n);
1626 valp = RARRAY_PTR(val);
1627 for (i = 0; i < n; i++)
1628 up->torsion.phi0[i] = NUM2DBL(rb_Float(valp[i])) * kDeg2Rad;
1630 } else rb_raise(rb_eMolbyError, "invalid member phi0");
1631 s_RegisterUndoForParameterAttrChange(self, s_Phi0Sym, val, oldval, oldsrc);
1636 static VALUE s_ParameterRef_SetA(VALUE self, VALUE val) {
1641 up = s_UnionParFromValue(self, &tp, 1);
1642 oldval = s_ParameterRef_GetA(self);
1643 oldsrc = up->bond.src;
1644 val = rb_Float(val);
1646 if (tp == kVdwParType)
1648 else if (tp == kVdwPairParType)
1650 else rb_raise(rb_eMolbyError, "invalid member A");
1651 s_RegisterUndoForParameterAttrChange(self, s_ASym, val, oldval, oldsrc);
1655 static VALUE s_ParameterRef_SetB(VALUE self, VALUE val) {
1660 up = s_UnionParFromValue(self, &tp, 1);
1661 oldval = s_ParameterRef_GetB(self);
1662 oldsrc = up->bond.src;
1663 val = rb_Float(val);
1665 if (tp == kVdwParType)
1667 else if (tp == kVdwPairParType)
1669 else rb_raise(rb_eMolbyError, "invalid member B");
1670 s_RegisterUndoForParameterAttrChange(self, s_BSym, val, oldval, oldsrc);
1675 static VALUE s_ParameterRef_SetReq(VALUE self, VALUE val) {
1680 up = s_UnionParFromValue(self, &tp, 1);
1681 oldval = s_ParameterRef_GetReq(self);
1682 oldsrc = up->bond.src;
1683 val = rb_Float(val);
1685 if (tp == kVdwParType) {
1687 up->vdw.A = pow(r * 2, 12.0) * up->vdw.eps;
1688 up->vdw.B = 2 * pow(r * 2, 6.0) * up->vdw.eps;
1689 } else if (tp == kVdwPairParType) {
1691 up->vdwp.A = pow(r * 2, 12.0) * up->vdwp.eps;
1692 up->vdwp.B = 2 * pow(r * 2, 6.0) * up->vdwp.eps;
1693 } else rb_raise(rb_eMolbyError, "invalid member r_eq");
1694 s_RegisterUndoForParameterAttrChange(self, s_ReqSym, val, oldval, oldsrc);
1698 static VALUE s_ParameterRef_SetEps(VALUE self, VALUE val) {
1703 up = s_UnionParFromValue(self, &tp, 1);
1704 oldval = s_ParameterRef_GetEps(self);
1705 oldsrc = up->bond.src;
1706 val = rb_Float(val);
1707 e = NUM2DBL(val) * KCAL2INTERNAL;
1708 if (tp == kVdwParType) {
1710 up->vdw.A = pow(up->vdw.r_eq * 2, 12.0) * up->vdw.eps;
1711 up->vdw.B = 2 * pow(up->vdw.r_eq * 2, 6.0) * up->vdw.eps;
1712 } else if (tp == kVdwPairParType) {
1714 up->vdwp.A = pow(up->vdwp.r_eq * 2, 12.0) * up->vdwp.eps;
1715 up->vdwp.B = 2 * pow(up->vdwp.r_eq * 2, 6.0) * up->vdwp.eps;
1716 } else rb_raise(rb_eMolbyError, "invalid member eps");
1717 s_RegisterUndoForParameterAttrChange(self, s_EpsSym, val, oldval, oldsrc);
1722 static VALUE s_ParameterRef_SetA14(VALUE self, VALUE val) {
1727 up = s_UnionParFromValue(self, &tp, 1);
1728 oldval = s_ParameterRef_GetA14(self);
1729 oldsrc = up->bond.src;
1730 val = rb_Float(val);
1732 if (tp == kVdwParType)
1734 else if (tp == kVdwPairParType)
1736 else rb_raise(rb_eMolbyError, "invalid member A14");
1737 s_RegisterUndoForParameterAttrChange(self, s_A14Sym, val, oldval, oldsrc);
1741 static VALUE s_ParameterRef_SetB14(VALUE self, VALUE val) {
1746 up = s_UnionParFromValue(self, &tp, 1);
1747 oldval = s_ParameterRef_GetB14(self);
1748 oldsrc = up->bond.src;
1749 val = rb_Float(val);
1751 if (tp == kVdwParType)
1753 else if (tp == kVdwPairParType)
1755 else rb_raise(rb_eMolbyError, "invalid member B14");
1756 s_RegisterUndoForParameterAttrChange(self, s_B14Sym, val, oldval, oldsrc);
1761 static VALUE s_ParameterRef_SetReq14(VALUE self, VALUE val) {
1766 up = s_UnionParFromValue(self, &tp, 1);
1767 oldval = s_ParameterRef_GetReq14(self);
1768 oldsrc = up->bond.src;
1769 val = rb_Float(val);
1771 if (tp == kVdwParType) {
1773 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1774 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1775 } else if (tp == kVdwPairParType) {
1776 up->vdwp.r_eq14 = r;
1777 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1778 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1779 } else rb_raise(rb_eMolbyError, "invalid member r_eq14");
1780 s_RegisterUndoForParameterAttrChange(self, s_Req14Sym, val, oldval, oldsrc);
1784 static VALUE s_ParameterRef_SetEps14(VALUE self, VALUE val) {
1789 up = s_UnionParFromValue(self, &tp, 1);
1790 oldval = s_ParameterRef_GetEps14(self);
1791 oldsrc = up->bond.src;
1792 val = rb_Float(val);
1793 e = NUM2DBL(val) * KCAL2INTERNAL;
1794 if (tp == kVdwParType) {
1796 up->vdw.A14 = pow(up->vdw.r_eq14 * 2, 12.0) * up->vdw.eps14;
1797 up->vdw.B14 = 2 * pow(up->vdw.r_eq14 * 2, 6.0) * up->vdw.eps14;
1798 } else if (tp == kVdwPairParType) {
1800 up->vdwp.A14 = pow(up->vdwp.r_eq14 * 2, 12.0) * up->vdwp.eps14;
1801 up->vdwp.B14 = 2 * pow(up->vdwp.r_eq14 * 2, 6.0) * up->vdwp.eps14;
1802 } else rb_raise(rb_eMolbyError, "invalid member eps14");
1803 s_RegisterUndoForParameterAttrChange(self, s_Eps14Sym, val, oldval, oldsrc);
1807 static VALUE s_ParameterRef_SetCutoff(VALUE self, VALUE val) {
1811 oldval = s_ParameterRef_GetCutoff(self);
1812 oldsrc = up->bond.src;
1813 up = s_UnionParFromValue(self, &tp, 1);
1814 val = rb_Float(val);
1815 if (tp == kVdwCutoffParType) {
1816 up->vdwcutoff.cutoff = NUM2DBL(val);
1817 } else rb_raise(rb_eMolbyError, "invalid member cutoff");
1818 s_RegisterUndoForParameterAttrChange(self, s_CutoffSym, val, oldval, oldsrc);
1822 static VALUE s_ParameterRef_SetRadius(VALUE self, VALUE val) {
1826 up = s_UnionParFromValue(self, &tp, 1);
1827 oldval = s_ParameterRef_GetRadius(self);
1828 oldsrc = up->bond.src;
1829 val = rb_Float(val);
1830 if (tp == kElementParType) {
1831 up->atom.radius = NUM2DBL(val);
1832 } else rb_raise(rb_eMolbyError, "invalid member radius");
1833 s_RegisterUndoForParameterAttrChange(self, s_RadiusSym, val, oldval, oldsrc);
1837 static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
1840 VALUE *valp, oldval;
1841 up = s_UnionParFromValue(self, &tp, 1);
1842 oldval = s_ParameterRef_GetColor(self);
1843 oldsrc = up->bond.src;
1844 val = rb_ary_to_ary(val);
1845 if (RARRAY_LEN(val) != 3)
1846 rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
1847 valp = RARRAY_PTR(val);
1848 if (tp == kElementParType) {
1849 up->atom.r = NUM2DBL(rb_Float(valp[0]));
1850 up->atom.g = NUM2DBL(rb_Float(valp[1]));
1851 up->atom.b = NUM2DBL(rb_Float(valp[2]));
1852 } else rb_raise(rb_eMolbyError, "invalid member color");
1853 s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
1857 static VALUE s_ParameterRef_SetAtomicNumber(VALUE self, VALUE val) {
1861 up = s_UnionParFromValue(self, &tp, 1);
1862 oldval = s_ParameterRef_GetAtomicNumber(self);
1863 oldsrc = up->bond.src;
1864 val = rb_Integer(val);
1865 if (tp == kElementParType)
1866 up->atom.number = NUM2INT(val);
1867 else if (tp == kVdwParType) {
1868 up->vdw.atomicNumber = NUM2INT(val);
1869 up->vdw.weight = WeightForAtomicNumber(up->vdw.atomicNumber);
1870 } else rb_raise(rb_eMolbyError, "invalid member atomic_number");
1871 s_RegisterUndoForParameterAttrChange(self, s_AtomicNumberSym, val, oldval, oldsrc);
1875 static VALUE s_ParameterRef_SetName(VALUE self, VALUE val) {
1879 up = s_UnionParFromValue(self, &tp, 1);
1880 oldval = s_ParameterRef_GetName(self);
1881 oldsrc = up->bond.src;
1882 if (tp == kElementParType) {
1883 strncpy(up->atom.name, StringValuePtr(val), 4);
1884 } else rb_raise(rb_eMolbyError, "invalid member name");
1885 s_RegisterUndoForParameterAttrChange(self, s_NameSym, val, oldval, oldsrc);
1889 static VALUE s_ParameterRef_SetWeight(VALUE self, VALUE val) {
1893 val = rb_Float(val);
1894 oldval = s_ParameterRef_GetWeight(self);
1895 up = s_UnionParFromValue(self, &tp, 1);
1896 oldsrc = up->bond.src;
1897 if (tp == kElementParType)
1898 up->atom.weight = NUM2DBL(val);
1899 else if (tp == kVdwParType)
1900 up->vdw.weight = NUM2DBL(val);
1901 else rb_raise(rb_eMolbyError, "invalid member weight");
1902 s_RegisterUndoForParameterAttrChange(self, s_WeightSym, val, oldval, oldsrc);
1906 static VALUE s_ParameterRef_SetFullName(VALUE self, VALUE val) {
1910 up = s_UnionParFromValue(self, &tp, 1);
1911 oldval = s_ParameterRef_GetFullName(self);
1912 oldsrc = up->bond.src;
1913 if (tp == kElementParType) {
1914 strncpy(up->atom.fullname, StringValuePtr(val), 15);
1915 up->atom.fullname[15] = 0;
1916 } else rb_raise(rb_eMolbyError, "invalid member fullname");
1917 s_RegisterUndoForParameterAttrChange(self, s_FullNameSym, val, oldval, oldsrc);
1921 static VALUE s_ParameterRef_SetComment(VALUE self, VALUE val) {
1923 Int tp, com, oldsrc;
1925 up = s_UnionParFromValue(self, &tp, 1);
1926 oldval = s_ParameterRef_GetComment(self);
1927 oldsrc = up->bond.src;
1931 com = ParameterCommentIndex(StringValuePtr(val));
1934 s_RegisterUndoForParameterAttrChange(self, s_CommentSym, val, oldval, oldsrc);
1938 /* Only false (undefined) and nil (local) can be set */
1939 static VALUE s_ParameterRef_SetSource(VALUE self, VALUE val) {
1943 up = s_UnionParFromValue(self, &tp, 1);
1944 if (val != Qfalse && val != Qnil)
1945 rb_raise(rb_eMolbyError, "set source: only false (undefined parameter) or nil (local parameter) is allowed");
1946 oldval = s_ParameterRef_GetSource(self);
1947 oldsrc = up->bond.src;
1948 if (oldsrc != 0 && oldsrc != -1)
1949 rb_raise(rb_eMolbyError, "source information of global parameter cannot be modified");
1950 up->bond.src = (val == Qfalse ? -1 : 0);
1951 s_RegisterUndoForParameterAttrChange(self, s_SourceSym, val, oldval, oldsrc);
1955 static struct s_ParameterAttrDef {
1957 VALUE *symref; /* Address of s_IndexSymbol etc. */
1958 ID id; /* Will be set within InitMolby() */
1959 VALUE (*getter)(VALUE);
1960 VALUE (*setter)(VALUE, VALUE);
1961 } s_ParameterAttrDefTable[] = {
1962 {"index", &s_IndexSym, 0, s_ParameterRef_GetIndex, NULL},
1963 {"par_type", &s_ParTypeSym, 0, s_ParameterRef_GetParType, NULL},
1964 {"atom_types", &s_AtomTypesSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1965 {"atom_type", &s_AtomTypeSym, 0, s_ParameterRef_GetAtomTypes, s_ParameterRef_SetAtomTypes},
1966 {"k", &s_KSym, 0, s_ParameterRef_GetK, s_ParameterRef_SetK},
1967 {"r0", &s_R0Sym, 0, s_ParameterRef_GetR0, s_ParameterRef_SetR0},
1968 {"a0", &s_A0Sym, 0, s_ParameterRef_GetA0, s_ParameterRef_SetA0},
1969 {"mult", &s_MultSym, 0, s_ParameterRef_GetMult, s_ParameterRef_SetMult},
1970 {"period", &s_PeriodSym, 0, s_ParameterRef_GetPeriod, s_ParameterRef_SetPeriod},
1971 {"phi0", &s_Phi0Sym, 0, s_ParameterRef_GetPhi0, s_ParameterRef_SetPhi0},
1972 /* {"A", &s_ASym, 0, s_ParameterRef_GetA, NULL},
1973 {"B", &s_BSym, 0, s_ParameterRef_GetB, NULL}, */
1974 {"r_eq", &s_ReqSym, 0, s_ParameterRef_GetReq, s_ParameterRef_SetReq},
1975 {"eps", &s_EpsSym, 0, s_ParameterRef_GetEps, s_ParameterRef_SetEps},
1976 /* {"A14", &s_A14Sym, 0, s_ParameterRef_GetA14, NULL},
1977 {"B14", &s_B14Sym, 0, s_ParameterRef_GetB14, NULL}, */
1978 {"r_eq14", &s_Req14Sym, 0, s_ParameterRef_GetReq14, s_ParameterRef_SetReq14},
1979 {"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
1980 {"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
1981 {"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
1982 {"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
1983 {"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
1984 {"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
1985 {"weight", &s_WeightSym, 0, s_ParameterRef_GetWeight, s_ParameterRef_SetWeight},
1986 {"fullname", &s_FullNameSym, 0, s_ParameterRef_GetFullName, s_ParameterRef_SetFullName},
1987 {"comment", &s_CommentSym, 0, s_ParameterRef_GetComment, s_ParameterRef_SetComment},
1988 {"source", &s_SourceSym, 0, s_ParameterRef_GetSource, s_ParameterRef_SetSource},
1989 {NULL} /* Sentinel */
1993 s_ParameterRef_SetAttr(VALUE self, VALUE key, VALUE value)
1997 if (TYPE(key) != T_SYMBOL) {
1998 kid = rb_intern(StringValuePtr(key));
2000 } else kid = SYM2ID(key);
2001 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
2002 if (s_ParameterAttrDefTable[i].id == kid) {
2003 if (value == Qundef)
2004 return (*(s_ParameterAttrDefTable[i].getter))(self);
2006 return (*(s_ParameterAttrDefTable[i].setter))(self, value);
2009 rb_raise(rb_eMolbyError, "unknown parameter attribute \"%s\"", rb_id2name(kid));
2010 return Qnil; /* not reached */
2014 s_ParameterRef_GetAttr(VALUE self, VALUE key)
2016 return s_ParameterRef_SetAttr(self, key, Qundef);
2021 * keys(idx) -> array of valid parameter attributes
2023 * Returns an array of valid parameter attributes (as Symbols).
2026 s_ParameterRef_Keys(VALUE self)
2029 Data_Get_Struct(self, ParameterRef, pref);
2030 switch (pref->parType) {
2032 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_R0Sym, s_CommentSym, s_SourceSym);
2034 return rb_ary_new3(7, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_KSym, s_A0Sym, s_CommentSym, s_SourceSym);
2035 case kDihedralParType:
2036 case kImproperParType:
2037 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_MultSym, s_KSym, s_PeriodSym, s_Phi0Sym, s_CommentSym, s_SourceSym);
2039 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);
2040 case kVdwPairParType:
2041 return rb_ary_new3(9, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_ReqSym, s_EpsSym, s_Req14Sym, s_Eps14Sym, s_CommentSym, s_SourceSym);
2042 case kVdwCutoffParType:
2043 return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
2044 case kElementParType:
2045 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);
2047 rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
2049 return Qnil; /* Not reached */
2054 * to_hash(idx) -> Hash
2056 * Returns a hash containing valid parameter names and values
2059 s_ParameterRef_ToHash(VALUE self)
2061 VALUE keys = s_ParameterRef_Keys(self);
2066 retval = rb_hash_new();
2067 for (i = 0; i < RARRAY_LEN(keys); i++) {
2068 VALUE key = RARRAY_PTR(keys)[i];
2069 VALUE val = s_ParameterRef_GetAttr(self, key);
2070 rb_hash_aset(retval, key, val);
2077 * parameter.to_s(idx) -> String
2079 * Returns a string representation of the given parameter
2082 s_ParameterRef_ToString(VALUE self)
2085 char buf[1024], types[4][8];
2086 UnionPar *up = s_UnionParFromValue(self, &tp, 0);
2089 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);
2092 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);
2094 case kDihedralParType:
2095 case kImproperParType:
2096 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]));
2098 for (i = 0; i < up->torsion.mult; i++) {
2099 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);
2104 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);
2106 case kVdwPairParType:
2107 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);
2109 case kVdwCutoffParType:
2110 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);
2112 case kElementParType:
2113 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);
2116 return rb_str_new2(buf);
2119 #pragma mark ====== Parameter Class ======
2121 /* The Parameter class actually encapsulate Molecule record. If the record pointer
2122 * is NULL, then the global parameters are looked for. */
2124 /* Rebuild the MD parameter record if necessary: may throw an exception */
2125 /* The second parameter is passed to md_arena.prepare; if true, then check only */
2127 s_RebuildMDParameterIfNecessary(VALUE val, VALUE cval)
2130 Data_Get_Struct(val, Molecule, mol);
2132 rb_raise(rb_eMolbyError, "the molecule is empty");
2133 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
2134 /* Do self.md_arena.prepare */
2135 VALUE val2 = rb_funcall(val, rb_intern("md_arena"), 0);
2137 val2 = rb_funcall(val2, rb_intern("prepare"), 1, cval);
2142 s_NewParameterValueFromValue(VALUE val)
2145 if (rb_obj_is_kind_of(val, rb_cMolecule)) {
2146 Data_Get_Struct(val, Molecule, mol);
2147 s_RebuildMDParameterIfNecessary(val, Qtrue);
2148 MoleculeRetain(mol);
2149 return Data_Wrap_Struct(rb_cParameter, 0, (void (*)(void *))MoleculeRelease, mol);
2152 return Data_Wrap_Struct(rb_cParameter, 0, NULL, mol);
2157 s_MoleculeFromParameterValue(VALUE val)
2160 Data_Get_Struct(val, Molecule, mol);
2165 s_ParameterFromParameterValue(VALUE val)
2168 Data_Get_Struct(val, Molecule, mol);
2171 return gBuiltinParameters;
2174 /* Forward declarations */
2175 static VALUE s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType);
2176 static Molecule *s_MoleculeFromParEnumerableValue(VALUE val);
2179 s_MoleculeFromParameterOrParEnumerableValue(VALUE val)
2181 if (rb_obj_is_kind_of(val, rb_cParameter)) {
2182 return s_MoleculeFromParameterValue(val);
2183 } else if (rb_obj_is_kind_of(val, rb_cParEnumerable)) {
2184 return s_MoleculeFromParEnumerableValue(val);
2190 * builtin -> Parameter
2192 * Returns a parameter value that points to the global (builtin) parameters.
2193 * Equivalent to Parameter::Builtin (constant).
2196 s_Parameter_Builtin(VALUE self)
2198 static ID s_builtin_id = 0;
2199 if (s_builtin_id == 0)
2200 s_builtin_id = rb_intern("Builtin");
2201 return rb_const_get(rb_cParameter, s_builtin_id);
2206 * bond(idx) -> ParameterRef
2208 * The index-th bond parameter record is returned.
2211 s_Parameter_Bond(VALUE self, VALUE ival)
2215 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2216 idx = NUM2INT(rb_Integer(ival));
2218 n = gBuiltinParameters->nbondPars;
2219 else if (mol->par != NULL)
2220 n = mol->par->nbondPars;
2222 if (idx < -n || idx >= n)
2223 rb_raise(rb_eMolbyError, "Bond parameter index (%d) out of range", idx);
2226 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, idx);
2231 * angle(idx) -> ParameterRef
2233 * The index-th angle parameter record is returned.
2236 s_Parameter_Angle(VALUE self, VALUE ival)
2240 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2241 idx = NUM2INT(rb_Integer(ival));
2243 n = gBuiltinParameters->nanglePars;
2244 else if (mol->par != NULL)
2245 n = mol->par->nanglePars;
2247 if (idx < -n || idx >= n)
2248 rb_raise(rb_eMolbyError, "Angle parameter index (%d) out of range", idx);
2251 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, idx);
2256 * dihedral(idx) -> ParameterRef
2258 * The index-th dihedral parameter record is returned.
2261 s_Parameter_Dihedral(VALUE self, VALUE ival)
2265 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2266 idx = NUM2INT(rb_Integer(ival));
2268 n = gBuiltinParameters->ndihedralPars;
2269 else if (mol->par != NULL)
2270 n = mol->par->ndihedralPars;
2272 if (idx < -n || idx >= n)
2273 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2276 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, idx);
2281 * improper(idx) -> ParameterRef
2283 * The index-th improper parameter record is returned.
2286 s_Parameter_Improper(VALUE self, VALUE ival)
2290 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2291 idx = NUM2INT(rb_Integer(ival));
2293 n = gBuiltinParameters->nimproperPars;
2294 else if (mol->par != NULL)
2295 n = mol->par->nimproperPars;
2297 if (idx < -n || idx >= n)
2298 rb_raise(rb_eMolbyError, "Improper parameter index (%d) out of range", idx);
2301 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, idx);
2306 * vdw(idx) -> ParameterRef
2308 * The index-th vdw parameter record is returned.
2311 s_Parameter_Vdw(VALUE self, VALUE ival)
2315 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2316 idx = NUM2INT(rb_Integer(ival));
2318 n = gBuiltinParameters->nvdwPars;
2319 else if (mol->par != NULL)
2320 n = mol->par->nvdwPars;
2322 if (idx < -n || idx >= n)
2323 rb_raise(rb_eMolbyError, "Vdw parameter index (%d) out of range", idx);
2326 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, idx);
2331 * vdw_pair(idx) -> ParameterRef
2333 * The index-th vdw pair parameter record is returned.
2336 s_Parameter_VdwPair(VALUE self, VALUE ival)
2340 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2341 idx = NUM2INT(rb_Integer(ival));
2343 n = gBuiltinParameters->nvdwpPars;
2344 else if (mol->par != NULL)
2345 n = mol->par->nvdwpPars;
2347 if (idx < -n || idx >= n)
2348 rb_raise(rb_eMolbyError, "Vdw pair parameter index (%d) out of range", idx);
2351 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwPairParType, idx);
2356 * vdw_cutoff(idx) -> ParameterRef
2358 * The index-th vdw cutoff parameter record is returned.
2361 s_Parameter_VdwCutoff(VALUE self, VALUE ival)
2365 mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2366 idx = NUM2INT(rb_Integer(ival));
2368 n = gBuiltinParameters->nvdwCutoffPars;
2369 else if (mol->par != NULL)
2370 n = mol->par->nvdwCutoffPars;
2372 if (idx < -n || idx >= n)
2373 rb_raise(rb_eMolbyError, "Dihedral parameter index (%d) out of range", idx);
2376 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwCutoffParType, idx);
2381 * element(idx) -> ParameterRef
2382 * element(t1) -> ParameterRef
2384 * In the first form, the index-th element parameter record is returned. In the second
2385 * form, the element parameter for t1 is looked up (the last index first). t1
2386 * is the element name string (up to 4 characters).
2387 * Unlike other Parameter methods, this is used only for the global parameter.
2390 s_Parameter_Element(VALUE self, VALUE ival)
2393 if (rb_obj_is_kind_of(ival, rb_cNumeric)) {
2394 int n = gCountElementParameters;
2395 idx1 = NUM2INT(rb_Integer(ival));
2396 if (idx1 < -n || idx1 >= n)
2397 rb_raise(rb_eMolbyError, "Element parameter index (%d) out of range", idx1);
2400 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, idx1);
2405 strncpy(name, StringValuePtr(ival), 4);
2407 for (i = gCountElementParameters - 1, ep = gElementParameters + i; i >= 0; i--) {
2408 if (strncmp(ep->name, name, 4) == 0)
2409 return ValueFromMoleculeWithParameterTypeAndIndex(NULL, kElementParType, i);
2419 * Returns the number of bond parameters.
2422 s_Parameter_Nbonds(VALUE self)
2425 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2427 n = gBuiltinParameters->nbondPars;
2428 else if (mol->par != NULL)
2429 n = mol->par->nbondPars;
2436 * nangles -> Integer
2438 * Returns the number of angle parameters.
2441 s_Parameter_Nangles(VALUE self)
2444 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2446 n = gBuiltinParameters->nanglePars;
2447 else if (mol->par != NULL)
2448 n = mol->par->nanglePars;
2455 * ndihedrals -> Integer
2457 * Returns the number of dihedral parameters.
2460 s_Parameter_Ndihedrals(VALUE self)
2463 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2465 n = gBuiltinParameters->ndihedralPars;
2466 else if (mol->par != NULL)
2467 n = mol->par->ndihedralPars;
2474 * nimpropers -> Integer
2476 * Returns the number of improper parameters.
2479 s_Parameter_Nimpropers(VALUE self)
2482 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2484 n = gBuiltinParameters->nimproperPars;
2485 else if (mol->par != NULL)
2486 n = mol->par->nimproperPars;
2495 * Returns the number of vdw parameters.
2498 s_Parameter_Nvdws(VALUE self)
2501 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2503 n = gBuiltinParameters->nvdwPars;
2504 else if (mol->par != NULL)
2505 n = mol->par->nvdwPars;
2512 * nvdw_pairs -> Integer
2514 * Returns the number of vdw pair parameters.
2517 s_Parameter_NvdwPairs(VALUE self)
2520 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2522 n = gBuiltinParameters->nvdwpPars;
2523 else if (mol->par != NULL)
2524 n = mol->par->nvdwpPars;
2531 * nvdw_cutoffs -> Integer
2533 * Returns the number of vdw cutoff parameters.
2536 s_Parameter_NvdwCutoffs(VALUE self)
2539 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2541 n = gBuiltinParameters->nvdwCutoffPars;
2542 else if (mol->par != NULL)
2543 n = mol->par->nvdwCutoffPars;
2550 * nelements -> Integer
2552 * Returns the number of element parameters.
2555 s_Parameter_Nelements(VALUE self)
2557 return INT2NUM(gCountElementParameters);
2562 * bonds -> ParEnumerable
2564 * Returns a ParEnumerable value that (formally) points to the collection of bond parameters.
2565 * Parameter.bonds[x] is equivalent to Parameter.bond(x). ParEnumerable class is
2566 * useful when all accessible parameters should be examined by use of 'each' method.
2569 s_Parameter_Bonds(VALUE self)
2571 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2572 return s_NewParEnumerableValueFromMoleculeAndType(mol, kBondParType);
2577 * angles -> ParEnumerable
2579 * Returns a ParEnumerable value that (formally) points to the collection of angle parameters.
2580 * Parameter.angles[x] is equivalent to Parameter.angle(x). ParEnumerable class is
2581 * useful when all accessible parameters should be examined by use of 'each' method.
2584 s_Parameter_Angles(VALUE self)
2586 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2587 return s_NewParEnumerableValueFromMoleculeAndType(mol, kAngleParType);
2592 * dihedrals -> ParEnumerable
2594 * Returns a ParEnumerable value that (formally) points to the collection of dihedral parameters.
2595 * Parameter.dihedrals[x] is equivalent to Parameter.dihedral(x). ParEnumerable class is
2596 * useful when all accessible parameters should be examined by use of 'each' method.
2599 s_Parameter_Dihedrals(VALUE self)
2601 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2602 return s_NewParEnumerableValueFromMoleculeAndType(mol, kDihedralParType);
2607 * impropers -> ParEnumerable
2609 * Returns a ParEnumerable value that (formally) points to the collection of improper parameters.
2610 * Parameter.impropers[x] is equivalent to Parameter.improper(x). ParEnumerable class is
2611 * useful when all accessible parameters should be examined by use of 'each' method.
2614 s_Parameter_Impropers(VALUE self)
2616 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2617 return s_NewParEnumerableValueFromMoleculeAndType(mol, kImproperParType);
2622 * vdws -> ParEnumerable
2624 * Returns a ParEnumerable value that (formally) points to the collection of vdw parameters.
2625 * Parameter.vdws[x] is equivalent to Parameter.vdw(x). ParEnumerable class is
2626 * useful when all accessible parameters should be examined by use of 'each' method.
2629 s_Parameter_Vdws(VALUE self)
2631 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2632 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwParType);
2637 * vdw_pairs -> ParEnumerable
2639 * Returns a ParEnumerable value that (formally) points to the collection of vdw pair parameters.
2640 * Parameter.vdw_pairs[x] is equivalent to Parameter.vdw_pair(x). ParEnumerable class is
2641 * useful when all accessible parameters should be examined by use of 'each' method.
2644 s_Parameter_VdwPairs(VALUE self)
2646 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2647 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwPairParType);
2652 * vdw_cutoffs -> ParEnumerable
2654 * Returns a ParEnumerable value that (formally) points to the collection of vdw cutoff parameters.
2655 * Parameter.vdw_cutoffs[x] is equivalent to Parameter.vdw_cutoff(x). ParEnumerable class is
2656 * useful when all accessible parameters should be examined by use of 'each' method.
2659 s_Parameter_VdwCutoffs(VALUE self)
2661 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2662 return s_NewParEnumerableValueFromMoleculeAndType(mol, kVdwCutoffParType);
2667 * elements -> ParEnumerable
2669 * Returns a ParEnumerable value that (formally) points to the collection of element parameters.
2670 * Parameter.elements[x] is equivalent to Parameter.element(x). ParEnumerable class is
2671 * useful when all accessible parameters should be examined by use of 'each' method.
2674 s_Parameter_Elements(VALUE self)
2676 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2677 return s_NewParEnumerableValueFromMoleculeAndType(mol, kElementParType);
2681 s_Parameter_Lookup_sub(int argc, VALUE *argv, int parType, Molecule *mol)
2683 VALUE atval, optval;
2685 int i, n, idx, flags, is_global;
2687 rb_scan_args(argc, argv, "1*", &atval, &optval);
2689 /* Get the atom types */
2691 case kBondParType: n = 2; break;
2692 case kAngleParType: n = 3; break;
2693 case kDihedralParType: n = 4; break;
2694 case kImproperParType: n = 4; break;
2695 case kVdwParType: n = 1; break;
2696 case kVdwPairParType: n = 2; break;
2697 default: return Qnil;
2699 s_ScanAtomTypes(atval, n, t);
2701 /* Analyze options */
2703 n = RARRAY_LEN(optval);
2704 for (i = 0; i < n; i++) {
2705 VALUE oval = RARRAY_PTR(optval)[i];
2706 if (oval == ID2SYM(rb_intern("global")))
2707 flags |= kParameterLookupGlobal;
2708 else if (oval == ID2SYM(rb_intern("local")))
2709 flags |= kParameterLookupLocal;
2710 else if (oval == ID2SYM(rb_intern("missing")))
2711 flags |= kParameterLookupMissing;
2712 else if (oval == ID2SYM(rb_intern("nowildcard")))
2713 flags |= kParameterLookupNoWildcard;
2714 else if (oval == ID2SYM(rb_intern("nobasetype")))
2715 flags |= kParameterLookupNoBaseAtomType;
2716 else if (oval == ID2SYM(rb_intern("create")))
2720 flags = kParameterLookupGlobal | kParameterLookupLocal;
2725 case kBondParType: {
2728 bp = ParameterLookupBondPar(mol->par, t[0], t[1], -1, -1, flags);
2730 idx = bp - mol->par->bondPars;
2734 bp = ParameterLookupBondPar(gBuiltinParameters, t[0], t[1], -1, -1, flags);
2736 idx = bp - gBuiltinParameters->bondPars;
2741 case kAngleParType: {
2744 ap = ParameterLookupAnglePar(mol->par, t[0], t[1], t[2], -1, -1, -1, flags);
2746 idx = ap - mol->par->anglePars;
2750 ap = ParameterLookupAnglePar(gBuiltinParameters, t[0], t[1], t[2], -1, -1, -1, flags);
2752 idx = ap - gBuiltinParameters->anglePars;
2757 case kDihedralParType: {
2760 tp = ParameterLookupDihedralPar(mol->par, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2762 idx = tp - mol->par->dihedralPars;
2766 tp = ParameterLookupDihedralPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2768 idx = tp - gBuiltinParameters->dihedralPars;
2773 case kImproperParType: {
2776 tp = ParameterLookupImproperPar(mol->par, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2778 idx = tp - mol->par->improperPars;
2782 tp = ParameterLookupImproperPar(gBuiltinParameters, t[0], t[1], t[2], t[3], -1, -1, -1, -1, flags);
2784 idx = tp - gBuiltinParameters->improperPars;
2792 vp = ParameterLookupVdwPar(mol->par, t[0], flags);
2794 idx = vp - mol->par->vdwPars;
2798 vp = ParameterLookupVdwPar(gBuiltinParameters, t[0], flags);
2800 idx = vp - gBuiltinParameters->vdwPars;
2805 case kVdwPairParType: {
2808 vp = ParameterLookupVdwPairPar(mol->par, t[0], t[1], flags);
2810 idx = vp - mol->par->vdwpPars;
2814 vp = ParameterLookupVdwPairPar(gBuiltinParameters, t[0], t[1], flags);
2816 idx = vp - gBuiltinParameters->vdwpPars;
2825 if ((flags & 256) == 0 || mol == NULL || mol->par == NULL)
2828 /* Insert a new parameter record */
2830 Int count = ParameterGetCountForType(mol->par, parType);
2831 IntGroup *ig = IntGroupNewWithPoints(count, 1, -1);
2832 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig, 0, NULL);
2833 IntGroupRelease(ig);
2836 /* Set atom types */
2837 up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx);
2842 up->bond.type1 = t[0];
2843 up->bond.type2 = t[1];
2846 up->angle.type1 = t[0];
2847 up->angle.type2 = t[1];
2848 up->angle.type3 = t[2];
2850 case kDihedralParType:
2851 case kImproperParType:
2852 up->torsion.type1 = t[0];
2853 up->torsion.type2 = t[1];
2854 up->torsion.type3 = t[2];
2855 up->torsion.type4 = t[3];
2858 up->vdw.type1 = t[0];
2860 case kVdwPairParType:
2861 up->vdwp.type1 = t[0];
2862 up->vdwp.type2 = t[1];
2869 return ValueFromMoleculeWithParameterTypeAndIndex(mol, parType, idx);
2874 * lookup(par_type, atom_types, options, ...) -> ParameterRef
2875 * lookup(par_type, atom_type_string, options, ...) -> ParameterRef
2877 * Find the parameter record that matches the given atom types. The atom types are given
2878 * either as an array of string, or a single string delimited by whitespaces or hyphens.
2879 * Options are given as symbols. Valid values are :global (look for global parameters), :local
2880 * (look for local parameters), :missing (look for missing parameters), :nowildcard (do not
2881 * allow wildcard matching), :nobasetype (the base type does not match for the variant types)
2884 s_Parameter_LookUp(int argc, VALUE *argv, VALUE self)
2887 Molecule *mol = s_MoleculeFromParameterOrParEnumerableValue(self);
2889 rb_raise(rb_eMolbyError, "parameter type and atom types must be specified");
2890 parType = s_ParTypeFromValue(argv[0]);
2891 return s_Parameter_Lookup_sub(argc - 1, argv + 1, parType, mol);
2894 #pragma mark ====== ParEnumerable Class ======
2896 /* The ParEnumerable class encapsulates the Molecule (not Parameter) pointer
2897 and the parameter type. If the Molecule is NULL, then it refers to the
2898 global (built-in) parameters. Note that, even when the Molecule is not NULL,
2899 the global parameters are always accessible. */
2901 typedef struct ParEnumerable {
2903 Int parType; /* Same as parType in ParameterRef */
2906 static ParEnumerable *
2907 s_ParEnumerableNew(Molecule *mol, Int parType)
2909 ParEnumerable *pen = (ParEnumerable *)calloc(sizeof(ParEnumerable), 1);
2913 MoleculeRetain(mol);
2914 pen->parType = parType;
2920 s_ParEnumerableRelease(ParEnumerable *pen)
2923 if (pen->mol != NULL)
2924 MoleculeRelease(pen->mol);
2930 s_MoleculeFromParEnumerableValue(VALUE val)
2933 Data_Get_Struct(val, ParEnumerable, pen);
2938 s_NewParEnumerableValueFromMoleculeAndType(Molecule *mol, Int parType)
2940 ParEnumerable *pen = s_ParEnumerableNew(mol, parType);
2942 rb_raise(rb_eMolbyError, "cannot allocate ParEnumerable record");
2943 return Data_Wrap_Struct(rb_cParEnumerable, 0, (void (*)(void *))s_ParEnumerableRelease, pen);
2948 * par_type -> String
2950 * Get the parameter type, like "bond", "angle", etc.
2953 s_ParEnumerable_ParType(VALUE self) {
2956 Data_Get_Struct(self, ParEnumerable, pen);
2958 if (tp == kElementParType)
2959 return rb_str_new2("element");
2960 tp -= kFirstParType;
2961 if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
2962 return rb_str_new2(s_ParameterTypeNames[tp]);
2963 else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
2968 * self[idx] -> ParameterRef
2970 * Call the accessor of the Parameter object from which this ParEnumerable object is derived from.
2971 * Thus, if self is "bond" type, self[idx] is equivalent to p.bond(idx), where p is the
2972 * parent Parameter object of self.
2974 * <b>See Also</b>: Parameter#bond, Parameter#angle, Parameter#dihedral, Parameter#improper,
2975 * Parameter#vdw, Parameter#vdw_pair, Parameter#vdw_cutoff, Parameter#element.
2978 s_ParEnumerable_Aref(VALUE self, VALUE ival)
2981 Data_Get_Struct(self, ParEnumerable, pen);
2982 switch (pen->parType) {
2983 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
2984 case kBondParType: return s_Parameter_Bond(self, ival);
2985 case kAngleParType: return s_Parameter_Angle(self, ival);
2986 case kDihedralParType: return s_Parameter_Dihedral(self, ival);
2987 case kImproperParType: return s_Parameter_Improper(self, ival);
2988 case kVdwParType: return s_Parameter_Vdw(self, ival);
2989 case kVdwPairParType: return s_Parameter_VdwPair(self, ival);
2990 case kVdwCutoffParType: return s_Parameter_VdwCutoff(self, ival);
2991 case kElementParType: return s_Parameter_Element(self, ival);
2993 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
2995 return Qnil; /* Not reached */
3002 * Returns the number of parameters included in this enumerable.
3005 s_ParEnumerable_Length(VALUE self)
3008 Data_Get_Struct(self, ParEnumerable, pen);
3009 switch (pen->parType) {
3010 /* s_Parameter_XXXX() also accepts ParEnumerable argument */
3011 case kBondParType: return s_Parameter_Nbonds(self);
3012 case kAngleParType: return s_Parameter_Nangles(self);
3013 case kDihedralParType: return s_Parameter_Ndihedrals(self);
3014 case kImproperParType: return s_Parameter_Nimpropers(self);
3015 case kVdwParType: return s_Parameter_Nvdws(self);
3016 case kVdwPairParType: return s_Parameter_NvdwPairs(self);
3017 case kVdwCutoffParType: return s_Parameter_NvdwCutoffs(self);
3018 case kElementParType: return s_Parameter_Nelements(self);
3020 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3022 return Qnil; /* Not reached */
3029 * Call the block for each parameter, passing a ParameterRef object as a block argument.
3032 s_ParEnumerable_Each(VALUE self)
3038 Data_Get_Struct(self, ParEnumerable, pen);
3039 if (pen->parType == kElementParType)
3040 n = gCountElementParameters;
3042 switch (pen->parType) {
3043 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3044 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3045 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3046 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3047 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3048 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3049 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3051 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3053 if (pen->mol == NULL)
3054 n = *((Int *)((char *)gBuiltinParameters + ofs));
3055 else if (pen->mol->par != NULL)
3056 n = *((Int *)((char *)(pen->mol->par) + ofs));
3059 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3060 Data_Get_Struct(aval, ParameterRef, pref);
3061 for (i = 0; i < n; i++) {
3070 * reverse_each {|pref| ...}
3072 * Call the block for each parameter in the reverse order, passing a ParameterRef object as a block argument.
3075 s_ParEnumerable_ReverseEach(VALUE self)
3081 Data_Get_Struct(self, ParEnumerable, pen);
3082 if (pen->parType == kElementParType)
3083 n = gCountElementParameters;
3085 switch (pen->parType) {
3086 case kBondParType: ofs = offsetof(Parameter, nbondPars); break;
3087 case kAngleParType: ofs = offsetof(Parameter, nanglePars); break;
3088 case kDihedralParType: ofs = offsetof(Parameter, ndihedralPars); break;
3089 case kImproperParType: ofs = offsetof(Parameter, nimproperPars); break;
3090 case kVdwParType: ofs = offsetof(Parameter, nvdwPars); break;
3091 case kVdwPairParType: ofs = offsetof(Parameter, nvdwpPars); break;
3092 case kVdwCutoffParType: ofs = offsetof(Parameter, nvdwCutoffPars); break;
3094 rb_raise(rb_eMolbyError, "internal error: unknown parameter type (%d)", pen->parType);
3096 if (pen->mol == NULL)
3097 n = *((Int *)((char *)gBuiltinParameters + ofs));
3098 else if (pen->mol->par != NULL)
3099 n = *((Int *)((char *)(pen->mol->par) + ofs));
3102 aval = ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, 0);
3103 Data_Get_Struct(aval, ParameterRef, pref);
3104 for (i = n - 1; i >= 0; i--) {
3113 * insert(idx = nil, pref = nil) -> ParameterRef
3115 * Insert a new parameter at the specified position (if idx is nil, then at the end).
3116 * If a ParameterRef is given, then the content of the parameter is copied to the new parameter,
3117 * and the parameter is set as molecule-local. Otherwise, the new parameter is blank, and the
3118 * parameter is left undefined.
3119 * Throws an exception if ParEnumerable points to the global parameter.
3122 s_ParEnumerable_Insert(int argc, VALUE *argv, VALUE self)
3130 Data_Get_Struct(self, ParEnumerable, pen);
3131 if (pen->mol == NULL)
3132 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3133 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3134 rb_scan_args(argc, argv, "02", &ival, &pval);
3136 i = NUM2INT(rb_Integer(ival));
3138 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3143 UnionPar *up = s_UnionParFromValue(pval, &type, 0);
3144 if (up == NULL || type != pen->parType)
3145 rb_raise(rb_eMolbyError, "the parameter specification is not correct");
3146 ParameterCopyOneWithType(&u, up, pen->parType);
3149 memset(&u, 0, sizeof(u));
3152 ig = IntGroupNewWithPoints(n, 1, -1);
3153 ParameterInsert(pen->mol->par, pen->parType, &u, ig);
3155 act = MolActionNew(gMolActionDeleteParameters, pen->parType, ig);
3156 MolActionCallback_registerUndo(pen->mol, act);
3157 MolActionRelease(act);
3159 IntGroupRelease(ig);
3160 return ValueFromMoleculeWithParameterTypeAndIndex(pen->mol, pen->parType, n);
3168 * Delete the parameter(s) specified by the argument.
3171 s_ParEnumerable_Delete(VALUE self, VALUE ival)
3176 Data_Get_Struct(self, ParEnumerable, pen);
3177 if (pen->mol == NULL)
3178 rb_raise(rb_eMolbyError, "the global parameters cannot be modified");
3179 n = ParameterGetCountForType(pen->mol->par, pen->parType);
3180 if (rb_obj_is_kind_of(ival, rb_cInteger)) {
3181 ig = IntGroupNewWithPoints(NUM2INT(ival), 1, -1);
3184 ig = IntGroupFromValue(ival);
3185 if ((i = IntGroupGetCount(ig)) == 0) {
3186 IntGroupRelease(ig);
3190 if ((i = IntGroupGetNthPoint(ig, i - 1)) >= n)
3191 rb_raise(rb_eMolbyError, "the parameter index (%d) out of range (should be 0..%d)", i, n);
3194 MolActionCreateAndPerform(pen->mol, gMolActionDeleteParameters, pen->parType, ig);
3195 IntGroupRelease(ig);
3201 * lookup(atom_types, options, ...) -> ParameterRef
3202 * lookup(atom_type_string, options, ...) -> ParameterRef
3204 * Find the parameter record that matches the given atom types. The arguments are
3205 * the same as Parameter#lookup, except for the parameter type which is implicitly
3209 s_ParEnumerable_LookUp(int argc, VALUE *argv, VALUE self)
3212 Data_Get_Struct(self, ParEnumerable, pen);
3213 return s_Parameter_Lookup_sub(argc, argv, pen->parType, pen->mol);
3216 #pragma mark ====== AtomRef Class ======
3218 /* Forward declaration for register undo */
3219 static VALUE s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self);
3221 /* Ruby string "set_atom_attr" */
3222 static VALUE s_SetAtomAttrString;
3225 s_AtomIndexFromValue(VALUE self, Atom **app, Molecule **mpp)
3229 Data_Get_Struct(self, AtomRef, aref);
3230 idx = (aref->idx >= 0 ? aref->idx : aref->mol->natoms + aref->idx);
3231 if (idx < 0 || idx >= aref->mol->natoms)
3232 rb_raise(rb_eMolbyError, "atom index out of range (%d; should be %d..%d)", aref->idx, -aref->mol->natoms, aref->mol->natoms - 1);
3234 *app = aref->mol->atoms + idx;
3241 s_AtomFromValue(VALUE self)
3244 s_AtomIndexFromValue(self, &ap, NULL);
3249 s_AtomAndMoleculeFromValue(VALUE self, Molecule **mpp)
3252 s_AtomIndexFromValue(self, &ap, mpp);
3257 s_NotifyModificationForAtomRef(VALUE self)
3260 Data_Get_Struct(self, AtomRef, aref);
3261 MoleculeIncrementModifyCount(aref->mol);
3265 s_RegisterUndoForAtomAttrChange(VALUE self, VALUE key, VALUE val, VALUE oldval)
3268 Data_Get_Struct(self, AtomRef, aref);
3269 if (MolActionCallback_isUndoRegistrationEnabled(aref->mol) && !RTEST(rb_funcall(val, s_ID_equal, 1, oldval))) {
3272 act = MolActionNew(SCRIPT_ACTION("rrr"), "set_atom_attr", INT2NUM(aref->idx), key, oldval);
3273 MolActionCallback_registerUndo(aref->mol, act);
3274 MoleculeCallback_notifyModification(aref->mol, 0);
3275 /* Request MD rebuilt if necessary */
3276 if (key == s_AtomTypeSym || key == s_ChargeSym || key == s_WeightSym || key == s_FixPosSym || key == s_FixForceSym)
3277 aref->mol->needsMDRebuild = 1;
3282 ValueFromMoleculeAndIndex(Molecule *mol, int idx)
3285 aref = AtomRefNew(mol, idx);
3286 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
3290 s_AtomRef_GetMolecule(VALUE self)
3293 s_AtomIndexFromValue(self, NULL, &mpp);
3294 return ValueFromMolecule(mpp);
3297 static VALUE s_AtomRef_GetIndex(VALUE self) {
3298 return INT2NUM(s_AtomIndexFromValue(self, NULL, NULL));
3301 static VALUE s_AtomRef_GetSegSeq(VALUE self) {
3302 return INT2NUM(s_AtomFromValue(self)->segSeq);
3305 static VALUE s_AtomRef_GetSegName(VALUE self) {
3306 char *p = s_AtomFromValue(self)->segName;
3307 return rb_str_new(p, strlen_limit(p, 4));
3310 static VALUE s_AtomRef_GetResSeq(VALUE self) {
3311 return INT2NUM(s_AtomFromValue(self)->resSeq);
3314 static VALUE s_AtomRef_GetResName(VALUE self) {
3315 char *p = s_AtomFromValue(self)->resName;
3316 return rb_str_new(p, strlen_limit(p, 4));
3319 static VALUE s_AtomRef_GetName(VALUE self) {
3320 char *p = s_AtomFromValue(self)->aname;
3321 return rb_str_new(p, strlen_limit(p, 4));
3324 static VALUE s_AtomRef_GetAtomType(VALUE self) {
3325 int type = s_AtomFromValue(self)->type;
3326 char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
3327 return rb_str_new(p, strlen_limit(p, 6));
3330 static VALUE s_AtomRef_GetCharge(VALUE self) {
3331 return rb_float_new(s_AtomFromValue(self)->charge);
3334 static VALUE s_AtomRef_GetWeight(VALUE self) {
3335 return rb_float_new(s_AtomFromValue(self)->weight);
3338 static VALUE s_AtomRef_GetElement(VALUE self) {
3339 char *p = s_AtomFromValue(self)->element;
3340 return rb_str_new(p, strlen_limit(p, 4));
3343 static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
3344 return INT2NUM(s_AtomFromValue(self)->atomicNumber);
3347 static VALUE s_AtomRef_GetConnects(VALUE self) {
3350 Atom *ap = s_AtomFromValue(self);
3351 retval = rb_ary_new();
3352 cp = AtomConnectData(&ap->connect);
3353 for (i = 0; i < ap->connect.count; i++)
3354 rb_ary_push(retval, INT2NUM(cp[i]));
3358 static VALUE s_AtomRef_GetR(VALUE self) {
3359 return ValueFromVector(&(s_AtomFromValue(self)->r));
3362 static VALUE s_AtomRef_GetX(VALUE self) {
3363 return rb_float_new(s_AtomFromValue(self)->r.x);
3366 static VALUE s_AtomRef_GetY(VALUE self) {
3367 return rb_float_new(s_AtomFromValue(self)->r.y);
3370 static VALUE s_AtomRef_GetZ(VALUE self) {
3371 return rb_float_new(s_AtomFromValue(self)->r.z);
3374 static Vector s_AtomRef_GetFractionalRAsVector(VALUE self) {
3378 s_AtomIndexFromValue(self, &ap, &mp);
3380 if (mp->cell != NULL)
3381 TransformVec(&r1, mp->cell->rtr, &r1);
3385 static VALUE s_AtomRef_GetFractionalR(VALUE self) {
3386 Vector r1 = s_AtomRef_GetFractionalRAsVector(self);
3387 return ValueFromVector(&r1);
3390 static VALUE s_AtomRef_GetFractionalX(VALUE self) {
3391 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).x);
3394 static VALUE s_AtomRef_GetFractionalY(VALUE self) {
3395 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).y);
3398 static VALUE s_AtomRef_GetFractionalZ(VALUE self) {
3399 return rb_float_new(s_AtomRef_GetFractionalRAsVector(self).z);
3402 static VALUE s_AtomRef_GetSigma(VALUE self) {
3403 return ValueFromVector(&(s_AtomFromValue(self)->sigma));
3406 static VALUE s_AtomRef_GetSigmaX(VALUE self) {
3407 return rb_float_new(s_AtomFromValue(self)->sigma.x);
3410 static VALUE s_AtomRef_GetSigmaY(VALUE self) {
3411 return rb_float_new(s_AtomFromValue(self)->sigma.y);
3414 static VALUE s_AtomRef_GetSigmaZ(VALUE self) {
3415 return rb_float_new(s_AtomFromValue(self)->sigma.z);
3418 static VALUE s_AtomRef_GetV(VALUE self) {
3419 return ValueFromVector(&(s_AtomFromValue(self)->v));
3422 static VALUE s_AtomRef_GetF(VALUE self) {
3423 return ValueFromVector(&(s_AtomFromValue(self)->f));
3426 static VALUE s_AtomRef_GetOccupancy(VALUE self) {
3427 return rb_float_new(s_AtomFromValue(self)->occupancy);
3430 static VALUE s_AtomRef_GetTempFactor(VALUE self) {
3431 return rb_float_new(s_AtomFromValue(self)->tempFactor);
3434 static VALUE s_AtomRef_GetAniso(VALUE self) {
3437 Atom *ap = s_AtomFromValue(self);
3438 if (ap->aniso == NULL)
3440 retval = rb_ary_new();
3441 for (i = 0; i < 6; i++)
3442 rb_ary_push(retval, rb_float_new(ap->aniso->bij[i]));
3443 if (ap->aniso->has_bsig) {
3444 rb_ary_push(retval, INT2NUM(0));
3445 for (i = 0; i < 6; i++)
3446 rb_ary_push(retval, rb_float_new(ap->aniso->bsig[i]));
3451 static VALUE s_AtomRef_GetSymop(VALUE self) {
3453 Atom *ap = s_AtomFromValue(self);
3454 if (!ap->symop.alive)
3456 retval = rb_ary_new();
3457 rb_ary_push(retval, INT2NUM(ap->symop.sym));
3458 rb_ary_push(retval, INT2NUM(ap->symop.dx));
3459 rb_ary_push(retval, INT2NUM(ap->symop.dy));
3460 rb_ary_push(retval, INT2NUM(ap->symop.dz));
3461 rb_ary_push(retval, INT2NUM(ap->symbase));
3465 static VALUE s_AtomRef_GetIntCharge(VALUE self) {
3466 return INT2NUM(s_AtomFromValue(self)->intCharge);
3469 static VALUE s_AtomRef_GetFixForce(VALUE self) {
3470 return rb_float_new(s_AtomFromValue(self)->fix_force * INTERNAL2KCAL);
3473 static VALUE s_AtomRef_GetFixPos(VALUE self) {
3474 return ValueFromVector(&(s_AtomFromValue(self)->fix_pos));
3477 static VALUE s_AtomRef_GetExclusion(VALUE self) {
3481 MDExclusion *exinfo;
3484 idx = s_AtomIndexFromValue(self, &ap, &mol);
3485 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
3486 VALUE mval = ValueFromMolecule(mol);
3487 s_RebuildMDParameterIfNecessary(mval, Qnil);
3489 if (mol->arena->exinfo == NULL)
3491 exinfo = mol->arena->exinfo + idx;
3492 exlist = mol->arena->exlist;
3493 retval = rb_ary_new();
3494 aval = rb_ary_new();
3495 for (i = exinfo->index1; i < exinfo->index2; i++) /* 1-2 exclusion */
3496 rb_ary_push(aval, INT2NUM(exlist[i]));
3497 rb_ary_push(retval, aval);
3498 aval = rb_ary_new();
3499 for (i = exinfo->index2; i < exinfo->index3; i++) /* 1-3 exclusion */
3500 rb_ary_push(aval, INT2NUM(exlist[i]));
3501 rb_ary_push(retval, aval);
3502 aval = rb_ary_new();
3503 for (i = exinfo->index3; i < (exinfo + 1)->index0; i++) /* 1-4 exclusion */
3504 rb_ary_push(aval, INT2NUM(exlist[i]));
3505 rb_ary_push(retval, aval);
3509 static VALUE s_AtomRef_GetMMExclude(VALUE self) {
3510 return INT2NUM(s_AtomFromValue(self)->mm_exclude);
3513 static VALUE s_AtomRef_GetPeriodicExclude(VALUE self) {
3514 return INT2NUM(s_AtomFromValue(self)->periodic_exclude);
3517 static VALUE s_AtomRef_GetHidden(VALUE self) {
3518 return ((s_AtomFromValue(self)->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3521 static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
3522 rb_raise(rb_eMolbyError, "index cannot be directly set");
3526 static VALUE s_AtomRef_SetSegSeq(VALUE self, VALUE val) {
3527 VALUE oval = s_AtomRef_GetSegSeq(self);
3528 val = rb_Integer(val);
3529 s_AtomFromValue(self)->segSeq = NUM2INT(val);
3530 s_RegisterUndoForAtomAttrChange(self, s_SegSeqSym, val, oval);
3534 static VALUE s_AtomRef_SetSegName(VALUE self, VALUE val) {
3535 char *p = StringValuePtr(val);
3536 VALUE oval = s_AtomRef_GetSegName(self);
3537 strncpy(s_AtomFromValue(self)->segName, p, 4);
3538 s_RegisterUndoForAtomAttrChange(self, s_SegNameSym, val, oval);
3542 static VALUE s_AtomRef_SetResSeqOrResName(VALUE self, VALUE val) {
3543 rb_raise(rb_eMolbyError, "residue number/names cannot be directly set. Use Molecule#assign_residue instead.");
3544 return val; /* Not reached */
3547 static VALUE s_AtomRef_SetName(VALUE self, VALUE val) {
3548 char *p = StringValuePtr(val);
3549 VALUE oval = s_AtomRef_GetName(self);
3550 strncpy(s_AtomFromValue(self)->aname, p, 4);
3551 s_RegisterUndoForAtomAttrChange(self, s_NameSym, val, oval);
3555 static VALUE s_AtomRef_SetAtomType(VALUE self, VALUE val) {
3557 char *p = StringValuePtr(val);
3558 VALUE oval = s_AtomRef_GetAtomType(self);
3559 int type = (p[0] == 0 ? 0 : AtomTypeEncodeToUInt(p));
3560 if (type != 0 && type < kAtomTypeMinimum)
3561 rb_raise(rb_eMolbyError, "Invalid atom type '%s'", p);
3562 s_AtomAndMoleculeFromValue(self, &mp)->type = type;
3563 mp->needsMDRebuild = 1;
3564 s_RegisterUndoForAtomAttrChange(self, s_AtomTypeSym, val, oval);
3568 static VALUE s_AtomRef_SetCharge(VALUE self, VALUE val) {
3570 VALUE oval = s_AtomRef_GetCharge(self);
3571 val = rb_Float(val);
3572 s_AtomAndMoleculeFromValue(self, &mp)->charge = NUM2DBL(val);
3573 mp->needsMDRebuild = 1;
3574 s_RegisterUndoForAtomAttrChange(self, s_ChargeSym, val, oval);
3578 static VALUE s_AtomRef_SetWeight(VALUE self, VALUE val) {
3580 VALUE oval = s_AtomRef_GetWeight(self);
3581 val = rb_Float(val);
3582 s_AtomAndMoleculeFromValue(self, &mp)->weight = NUM2DBL(val);
3583 mp->needsMDRebuild = 1;
3584 s_RegisterUndoForAtomAttrChange(self, s_WeightSym, val, oval);
3588 static VALUE s_AtomRef_SetElement(VALUE self, VALUE val) {
3591 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3592 char *p = StringValuePtr(val);
3593 VALUE oval = s_AtomRef_GetElement(self);
3594 ap->atomicNumber = ElementToInt(p);
3595 ElementToString(ap->atomicNumber, ap->element);
3596 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3598 s_RegisterUndoForAtomAttrChange(self, s_ElementSym, val, oval);
3599 mp->needsMDRebuild = 1;
3603 static VALUE s_AtomRef_SetAtomicNumber(VALUE self, VALUE val) {
3606 Atom *ap = s_AtomAndMoleculeFromValue(self, &mp);
3607 VALUE oval = s_AtomRef_GetAtomicNumber(self);
3608 val = rb_Integer(val);
3609 ap->atomicNumber = NUM2INT(val);
3610 ElementToString(ap->atomicNumber, ap->element);
3611 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3613 s_RegisterUndoForAtomAttrChange(self, s_AtomicNumberSym, val, oval);
3614 mp->needsMDRebuild = 1;
3618 static VALUE s_AtomRef_SetConnects(VALUE self, VALUE val) {
3619 rb_raise(rb_eMolbyError, "connection table cannot be directly set. Use Molecule#create_bond instead.");
3620 return val; /* Not reached */
3623 static VALUE s_AtomRef_SetR(VALUE self, VALUE val) {
3626 VALUE oval = s_AtomRef_GetR(self);
3627 VectorFromValue(val, &v);
3628 s_AtomAndMoleculeFromValue(self, &mp)->r = v;
3629 s_RegisterUndoForAtomAttrChange(self, s_RSym, val, oval);
3630 mp->needsMDCopyCoordinates = 1;
3634 static VALUE s_AtomRef_SetX(VALUE self, VALUE val) {
3637 VALUE oval = s_AtomRef_GetX(self);
3638 val = rb_Float(val);
3640 s_AtomAndMoleculeFromValue(self, &mp)->r.x = f;
3641 s_RegisterUndoForAtomAttrChange(self, s_XSym, val, oval);
3642 mp->needsMDCopyCoordinates = 1;
3646 static VALUE s_AtomRef_SetY(VALUE self, VALUE val) {
3649 VALUE oval = s_AtomRef_GetY(self);
3650 val = rb_Float(val);
3652 s_AtomAndMoleculeFromValue(self, &mp)->r.y = f;
3653 s_RegisterUndoForAtomAttrChange(self, s_YSym, val, oval);
3654 mp->needsMDCopyCoordinates = 1;
3658 static VALUE s_AtomRef_SetZ(VALUE self, VALUE val) {
3661 VALUE oval = s_AtomRef_GetZ(self);
3662 val = rb_Float(val);
3664 s_AtomAndMoleculeFromValue(self, &mp)->r.z = f;
3665 s_RegisterUndoForAtomAttrChange(self, s_ZSym, val, oval);
3666 mp->needsMDCopyCoordinates = 1;
3670 static VALUE s_AtomRef_SetFractionalR(VALUE self, VALUE val) {
3674 s_AtomIndexFromValue(self, &ap, &mp);
3676 VectorFromValue(val, &v);
3677 if (mp->cell != NULL)
3678 TransformVec(&v, mp->cell->tr, &v);
3680 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3681 mp->needsMDCopyCoordinates = 1;
3685 static VALUE s_AtomRef_SetFractionalX(VALUE self, VALUE val) {
3690 s_AtomIndexFromValue(self, &ap, &mp);
3692 val = rb_Float(val);
3694 if (mp->cell != NULL) {
3695 TransformVec(&v, mp->cell->rtr, &v);
3697 TransformVec(&v, mp->cell->tr, &v);
3700 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3701 mp->needsMDCopyCoordinates = 1;
3705 static VALUE s_AtomRef_SetFractionalY(VALUE self, VALUE val) {
3710 s_AtomIndexFromValue(self, &ap, &mp);
3712 val = rb_Float(val);
3714 if (mp->cell != NULL) {
3715 TransformVec(&v, mp->cell->rtr, &v);
3717 TransformVec(&v, mp->cell->tr, &v);
3720 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3721 mp->needsMDCopyCoordinates = 1;
3725 static VALUE s_AtomRef_SetFractionalZ(VALUE self, VALUE val) {
3730 s_AtomIndexFromValue(self, &ap, &mp);
3732 val = rb_Float(val);
3734 if (mp->cell != NULL) {
3735 TransformVec(&v, mp->cell->rtr, &v);
3737 TransformVec(&v, mp->cell->tr, &v);
3740 s_RegisterUndoForAtomAttrChange(self, s_RSym, Qnil, ValueFromVector(&ov));
3741 mp->needsMDCopyCoordinates = 1;
3745 static VALUE s_AtomRef_SetSigma(VALUE self, VALUE val) {
3748 VALUE oval = s_AtomRef_GetSigma(self);
3749 VectorFromValue(val, &v);
3750 s_AtomAndMoleculeFromValue(self, &mp)->sigma = v;
3751 s_RegisterUndoForAtomAttrChange(self, s_SigmaSym, val, oval);
3752 mp->needsMDCopyCoordinates = 1;
3756 static VALUE s_AtomRef_SetSigmaX(VALUE self, VALUE val) {
3759 VALUE oval = s_AtomRef_GetSigmaX(self);
3760 val = rb_Float(val);
3762 s_AtomAndMoleculeFromValue(self, &mp)->sigma.x = f;
3763 s_RegisterUndoForAtomAttrChange(self, s_SigmaXSym, val, oval);
3764 mp->needsMDCopyCoordinates = 1;
3768 static VALUE s_AtomRef_SetSigmaY(VALUE self, VALUE val) {
3771 VALUE oval = s_AtomRef_GetSigmaY(self);
3772 val = rb_Float(val);
3774 s_AtomAndMoleculeFromValue(self, &mp)->sigma.y = f;
3775 s_RegisterUndoForAtomAttrChange(self, s_SigmaYSym, val, oval);
3776 mp->needsMDCopyCoordinates = 1;
3780 static VALUE s_AtomRef_SetSigmaZ(VALUE self, VALUE val) {
3783 VALUE oval = s_AtomRef_GetSigmaZ(self);
3784 val = rb_Float(val);
3786 s_AtomAndMoleculeFromValue(self, &mp)->sigma.z = f;
3787 s_RegisterUndoForAtomAttrChange(self, s_SigmaZSym, val, oval);
3788 mp->needsMDCopyCoordinates = 1;
3792 static VALUE s_AtomRef_SetV(VALUE self, VALUE val) {
3796 VALUE oval = s_AtomRef_GetV(self);
3797 VectorFromValue(val, &v);
3798 s_AtomIndexFromValue(self, &ap, &mp);
3800 s_RegisterUndoForAtomAttrChange(self, s_VSym, val, oval);
3801 mp->needsMDCopyCoordinates = 1;
3805 static VALUE s_AtomRef_SetF(VALUE self, VALUE val) {
3808 VALUE oval = s_AtomRef_GetF(self);
3809 VectorFromValue(val, &v);
3810 s_AtomAndMoleculeFromValue(self, &mp)->f = v;
3811 s_RegisterUndoForAtomAttrChange(self, s_FSym, val, oval);
3812 mp->needsMDCopyCoordinates = 1;
3816 static VALUE s_AtomRef_SetOccupancy(VALUE self, VALUE val) {
3817 VALUE oval = s_AtomRef_GetOccupancy(self);
3819 val = rb_Float(val);
3820 s_AtomAndMoleculeFromValue(self, &mp)->occupancy = NUM2DBL(val);
3821 s_RegisterUndoForAtomAttrChange(self, s_OccupancySym, val, oval);
3822 mp->needsMDCopyCoordinates = 1; /* Occupancy can be used to exclude particular atoms from MM/MD */
3826 static VALUE s_AtomRef_SetTempFactor(VALUE self, VALUE val) {
3827 VALUE oval = s_AtomRef_GetTempFactor(self);
3828 val = rb_Float(val);
3829 s_AtomFromValue(self)->tempFactor = NUM2DBL(val);
3830 s_RegisterUndoForAtomAttrChange(self, s_TempFactorSym, val, oval);
3834 static VALUE s_AtomRef_SetAniso(VALUE self, VALUE val) {
3839 VALUE oval = s_AtomRef_GetAniso(self);
3840 Data_Get_Struct(self, AtomRef, aref);
3841 val = rb_funcall(val, rb_intern("to_a"), 0);
3842 n = RARRAY_LEN(val);
3843 valp = RARRAY_PTR(val);
3844 for (i = 0; i < 6; i++) {
3846 f[i] = NUM2DBL(rb_Float(valp[i]));
3850 type = NUM2INT(rb_Integer(valp[6]));
3853 for (i = 0; i < 6; i++)
3854 f[i + 6] = NUM2DBL(rb_Float(valp[i + 7]));
3856 for (i = 0; i < 6; i++)
3859 i = s_AtomIndexFromValue(self, NULL, NULL);
3860 MoleculeSetAniso(aref->mol, i, type, f[0], f[1], f[2], f[3], f[4], f[5], (n >= 13 ? f + 6 : NULL));
3861 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oval);
3865 static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
3871 VALUE oval = s_AtomRef_GetSymop(self);
3872 i = s_AtomIndexFromValue(self, &ap, &mol);
3874 ival[0] = ival[1] = ival[2] = ival[3] = ival[4] = 0;
3876 val = rb_funcall(val, rb_intern("to_a"), 0);
3877 n = RARRAY_LEN(val);
3878 valp = RARRAY_PTR(val);
3879 for (i = 0; i < 5; i++) {
3881 if (valp[i] == Qnil)
3884 ival[i] = NUM2INT(rb_Integer(valp[i]));
3885 } else ival[i] = -100000;
3888 if (ival[0] != -100000 && (ival[0] < 0 || ival[0] >= mol->nsyms))
3889 rb_raise(rb_eMolbyError, "index of symmetry (%d) is out of range (should be 0..%d)", ival[0], mol->nsyms - 1);
3890 if (ival[4] != -100000 && (ival[4] < 0 || ival[4] >= mol->natoms))
3891 rb_raise(rb_eMolbyError, "atom index number (%d) is out of range (should be 0..%d)", ival[4], mol->natoms - 1);
3892 if (ap->symop.sym == ival[0] && ap->symop.dx == ival[1] && ap->symop.dy == ival[2] && ap->symop.dz == ival[3])
3893 return val; /* No need to change */
3894 if (ival[0] != -100000)
3895 ap->symop.sym = ival[0];
3896 if (ival[1] != -100000)
3897 ap->symop.dx = ival[1];
3898 if (ival[2] != -100000)
3899 ap->symop.dy = ival[2];
3900 if (ival[3] != -100000)
3901 ap->symop.dz = ival[3];
3902 ap->symop.alive = (SYMOP_ALIVE(ap->symop) != 0);
3903 if (ival[4] != -100000)
3904 ap->symbase = ival[4];
3905 if (ap->symop.alive && (ap->aniso != NULL || (ATOM_AT_INDEX(mol->atoms, ap->symbase))->aniso != NULL)) {
3906 /* The anisotropic parameters should be recalculated */
3907 VALUE oaval = s_AtomRef_GetAniso(self);
3908 MoleculeSetAnisoBySymop(mol, i);
3909 s_RegisterUndoForAtomAttrChange(self, s_AnisoSym, val, oaval);
3911 s_RegisterUndoForAtomAttrChange(self, s_SymopSym, val, oval);
3915 static VALUE s_AtomRef_SetIntCharge(VALUE self, VALUE val) {
3916 VALUE oval = s_AtomRef_GetIntCharge(self);
3917 val = rb_Integer(val);
3918 s_AtomFromValue(self)->intCharge = NUM2INT(val);
3919 s_RegisterUndoForAtomAttrChange(self, s_IntChargeSym, val, oval);
3923 static VALUE s_AtomRef_SetFixForce(VALUE self, VALUE val) {
3925 VALUE oval = s_AtomRef_GetFixForce(self);
3926 val = rb_Float(val);
3927 s_AtomAndMoleculeFromValue(self, &mp)->fix_force = NUM2DBL(val) * KCAL2INTERNAL;
3928 s_RegisterUndoForAtomAttrChange(self, s_FixForceSym, val, oval);
3929 mp->needsMDRebuild = 1;
3933 static VALUE s_AtomRef_SetFixPos(VALUE self, VALUE val) {
3936 VALUE oval = s_AtomRef_GetFixPos(self);
3937 VectorFromValue(val, &v);
3938 s_AtomAndMoleculeFromValue(self, &mp)->fix_pos = v;
3939 s_RegisterUndoForAtomAttrChange(self, s_FixPosSym, val, oval);
3940 mp->needsMDRebuild = 1;
3944 static VALUE s_AtomRef_SetExclusion(VALUE self, VALUE val) {
3945 rb_raise(rb_eMolbyError, "exclusion table is read-only.");
3946 return val; /* Not reached */
3949 static VALUE s_AtomRef_SetMMExclude(VALUE self, VALUE val) {
3950 VALUE oval = s_AtomRef_GetIntCharge(self);
3951 val = rb_Integer(val);
3952 s_AtomFromValue(self)->mm_exclude = NUM2INT(val);
3953 s_RegisterUndoForAtomAttrChange(self, s_MMExcludeSym, val, oval);
3957 static VALUE s_AtomRef_SetPeriodicExclude(VALUE self, VALUE val) {
3958 VALUE oval = s_AtomRef_GetIntCharge(self);
3959 val = rb_Integer(val);
3960 s_AtomFromValue(self)->periodic_exclude = NUM2INT(val);
3961 s_RegisterUndoForAtomAttrChange(self, s_PeriodicExcludeSym, val, oval);
3965 static VALUE s_AtomRef_SetHidden(VALUE self, VALUE val) {
3966 Atom *ap = s_AtomFromValue(self);
3967 VALUE oval = ((ap->exflags & kAtomHiddenFlag) != 0 ? Qtrue : Qfalse);
3969 ap->exflags |= kAtomHiddenFlag;
3971 ap->exflags &= ~kAtomHiddenFlag;
3973 s_RegisterUndoForAtomAttrChange(self, s_HiddenSym, val, oval);
3977 static struct s_AtomAttrDef {
3979 VALUE *symref; /* Address of s_IndexSymbol etc. */
3980 ID id; /* Will be set within InitMolby() */
3981 VALUE (*getter)(VALUE);
3982 VALUE (*setter)(VALUE, VALUE);
3983 } s_AtomAttrDefTable[] = {
3984 {"index", &s_IndexSym, 0, s_AtomRef_GetIndex, s_AtomRef_SetIndex},
3985 {"seg_seq", &s_SegSeqSym, 0, s_AtomRef_GetSegSeq, s_AtomRef_SetSegSeq},
3986 {"seg_name", &s_SegNameSym, 0, s_AtomRef_GetSegName, s_AtomRef_SetSegName},
3987 {"res_seq", &s_ResSeqSym, 0, s_AtomRef_GetResSeq, s_AtomRef_SetResSeqOrResName},
3988 {"res_name", &s_ResNameSym, 0, s_AtomRef_GetResName, s_AtomRef_SetResSeqOrResName},
3989 {"name", &s_NameSym, 0, s_AtomRef_GetName, s_AtomRef_SetName},
3990 {"atom_type", &s_AtomTypeSym, 0, s_AtomRef_GetAtomType, s_AtomRef_SetAtomType},
3991 {"charge", &s_ChargeSym, 0, s_AtomRef_GetCharge, s_AtomRef_SetCharge},
3992 {"weight", &s_WeightSym, 0, s_AtomRef_GetWeight, s_AtomRef_SetWeight},
3993 {"element", &s_ElementSym, 0, s_AtomRef_GetElement, s_AtomRef_SetElement},
3994 {"atomic_number", &s_AtomicNumberSym,0, s_AtomRef_GetAtomicNumber, s_AtomRef_SetAtomicNumber},
3995 {"connects", &s_ConnectsSym, 0, s_AtomRef_GetConnects, s_AtomRef_SetConnects},
3996 {"r", &s_RSym, 0, s_AtomRef_GetR, s_AtomRef_SetR},
3997 {"x", &s_XSym, 0, s_AtomRef_GetX, s_AtomRef_SetX},
3998 {"y", &s_YSym, 0, s_AtomRef_GetY, s_AtomRef_SetY},
3999 {"z", &s_ZSym, 0, s_AtomRef_GetZ, s_AtomRef_SetZ},
4000 {"fract_r", &s_FractRSym, 0, s_AtomRef_GetFractionalR, s_AtomRef_SetFractionalR},
4001 {"fract_x", &s_FractXSym, 0, s_AtomRef_GetFractionalX, s_AtomRef_SetFractionalX},
4002 {"fract_y", &s_FractYSym, 0, s_AtomRef_GetFractionalY, s_AtomRef_SetFractionalY},
4003 {"fract_z", &s_FractZSym, 0, s_AtomRef_GetFractionalZ, s_AtomRef_SetFractionalZ},
4004 {"sigma", &s_SigmaSym, 0, s_AtomRef_GetSigma, s_AtomRef_SetSigma},
4005 {"sigma_x", &s_SigmaXSym, 0, s_AtomRef_GetSigmaX, s_AtomRef_SetSigmaX},
4006 {"sigma_y", &s_SigmaYSym, 0, s_AtomRef_GetSigmaY, s_AtomRef_SetSigmaY},
4007 {"sigma_z", &s_SigmaZSym, 0, s_AtomRef_GetSigmaZ, s_AtomRef_SetSigmaZ},
4008 {"v", &s_VSym, 0, s_AtomRef_GetV, s_AtomRef_SetV},
4009 {"f", &s_FSym, 0, s_AtomRef_GetF, s_AtomRef_SetF},
4010 {"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
4011 {"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
4012 {"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
4013 {"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
4014 {"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
4015 {"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
4016 {"fix_pos", &s_FixPosSym, 0, s_AtomRef_GetFixPos, s_AtomRef_SetFixPos},
4017 {"exclusion", &s_ExclusionSym, 0, s_AtomRef_GetExclusion, s_AtomRef_SetExclusion},
4018 {"mm_exclude", &s_MMExcludeSym, 0, s_AtomRef_GetMMExclude, s_AtomRef_SetMMExclude},
4019 {"periodic_exclude", &s_PeriodicExcludeSym, 0, s_AtomRef_GetPeriodicExclude, s_AtomRef_SetPeriodicExclude},
4020 {"hidden", &s_HiddenSym, 0, s_AtomRef_GetHidden, s_AtomRef_SetHidden},
4021 {NULL} /* Sentinel */
4025 s_AtomRef_SetAttr(VALUE self, VALUE key, VALUE value)
4029 if (TYPE(key) != T_SYMBOL) {
4030 kid = rb_intern(StringValuePtr(key));
4032 } else kid = SYM2ID(key);
4033 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
4034 if (s_AtomAttrDefTable[i].id == kid) {
4035 if (value == Qundef)
4036 return (*(s_AtomAttrDefTable[i].getter))(self);
4038 return (*(s_AtomAttrDefTable[i].setter))(self, value);
4041 rb_raise(rb_eMolbyError, "unknown atom attribute \"%s\"", rb_id2name(kid));
4042 return Qnil; /* not reached */
4046 s_AtomRef_GetAttr(VALUE self, VALUE key)
4048 return s_AtomRef_SetAttr(self, key, Qundef);
4051 #pragma mark ====== MolEnumerable Class ======
4053 static int s_Molecule_AtomIndexFromValue(Molecule *, VALUE);
4057 * self[idx] -> AtomRef or Array of Integers
4059 * Get the idx-th atom, bond, etc. for the Molecule from which this MolEnuerable object is
4060 * derived from. For the atom, the return value is AtomRef. For the residue, the return
4061 * value is a String. Otherwise, the return value is an Array of Integers.
4064 s_MolEnumerable_Aref(VALUE self, VALUE arg1)
4066 MolEnumerable *mseq;
4069 Data_Get_Struct(self, MolEnumerable, mseq);
4071 if (mseq->kind == kAtomKind) {
4072 return ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, arg1));
4074 idx1 = NUM2INT(arg1);
4075 switch (mseq->kind) {
4077 idx2 = (idx1 >= 0 ? idx1 : mol->nbonds + idx1);
4078 if (idx2 < 0 || idx2 >= mol->nbonds)
4079 rb_raise(rb_eIndexError, "bond index out of range (%d; should be %d..%d)", idx1, -mol->nbonds, mol->nbonds - 1);
4080 return rb_ary_new3(2, INT2NUM(mol->bonds[idx2 * 2]), INT2NUM(mol->bonds[idx2 * 2 + 1]));
4083 idx2 = (idx1 >= 0 ? idx1 : mol->nangles + idx1);
4084 if (idx2 < 0 || idx2 >= mol->nangles)
4085 rb_raise(rb_eIndexError, "angle index out of range (%d; should be %d..%d)", idx1, -mol->nangles, mol->nangles - 1);
4086 return rb_ary_new3(3, INT2NUM(mol->angles[idx2 * 3]), INT2NUM(mol->angles[idx2 * 3 + 1]), INT2NUM(mol->angles[idx2 * 3 + 2]));
4088 case kDihedralKind: {
4089 idx2 = (idx1 >= 0 ? idx1 : mol->ndihedrals + idx1);
4090 if (idx2 < 0 || idx2 >= mol->ndihedrals)
4091 rb_raise(rb_eIndexError, "dihedral index out of range (%d; should be %d..%d)", idx1, -mol->ndihedrals, mol->ndihedrals - 1);
4092 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]));
4094 case kImproperKind: {
4095 idx2 = (idx1 >= 0 ? idx1 : mol->nimpropers + idx1);
4096 if (idx2 < 0 || idx2 >= mol->nimpropers)
4097 rb_raise(rb_eIndexError, "improper index out of range (%d; should be %d..%d)", idx1, -mol->nimpropers, mol->nimpropers - 1);
4098 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]));
4100 case kResidueKind: {
4102 idx2 = (idx1 >= 0 ? idx1 : mol->nresidues + idx1);
4103 if (idx2 < 0 || idx2 >= mol->nresidues)
4104 rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
4105 p = mol->residues[idx2];
4106 return rb_str_new(p, strlen_limit(p, 4));
4116 * Returns the number of objects included in this enumerable.
4119 s_MolEnumerable_Length(VALUE self)
4121 MolEnumerable *mseq;
4122 Data_Get_Struct(self, MolEnumerable, mseq);
4123 switch (mseq->kind) {
4125 return INT2NUM(mseq->mol->natoms);
4127 return INT2NUM(mseq->mol->nbonds);
4129 return INT2NUM(mseq->mol->nangles);
4131 return INT2NUM(mseq->mol->ndihedrals);
4133 return INT2NUM(mseq->mol->nimpropers);
4135 return INT2NUM(mseq->mol->nresidues);
4144 * Call the block for each atom/bond/angle/dihedral/improper/residue. The block argument is
4145 * an AtomRef for atoms, a String for residues, and an Array of Integers for others.
4146 * For the atoms, a same AtomRef object is passed (with different internal information)
4147 * for each invocation of block. Otherwise, a new Ruby object will be created and passed
4148 * for each iteration.
4151 s_MolEnumerable_Each(VALUE self)
4153 MolEnumerable *mseq;
4155 int len = NUM2INT(s_MolEnumerable_Length(self));
4156 Data_Get_Struct(self, MolEnumerable, mseq);
4157 if (mseq->kind == kAtomKind) {
4158 /* The same AtomRef object will be used during the loop */
4159 AtomRef *aref = AtomRefNew(mseq->mol, 0);
4160 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
4161 for (i = 0; i < len; i++) {
4166 /* A new ruby object will be created at each iteration (not very efficient) */
4167 for (i = 0; i < len; i++) {
4168 rb_yield(s_MolEnumerable_Aref(self, INT2NUM(i)));
4174 #pragma mark ====== Molecule Class ======
4177 MoleculeFromValue(VALUE val)
4180 if (!rb_obj_is_kind_of(val, rb_cMolecule))
4181 rb_raise(rb_eMolbyError, "Molecule instance is expected");
4182 Data_Get_Struct(val, Molecule, mol);
4186 static VALUE sMoleculeRetainArray = Qnil;
4188 /* The function is called from MoleculeRelease() */
4189 /* The Ruby Molecule object is held as mol->exmolobj, and is also protected from */
4190 /* GC by registering in the sMoleculeRetainArray. This causes the same Ruby */
4191 /* object is always returned for the same Molecule. */
4192 /* When the reference count of the Molecule becomes 1, then the Ruby object is */
4193 /* removed from sMoleculeRetainArray. In this situation, the Molecule is retained */
4194 /* only by the currently alive Ruby containers. When the Ruby Molecule object is */
4195 /* removed from all alive Ruby containers, the Ruby object will be collected by */
4196 /* the next GC invocation, and at that time the Molecule structure is properly released. */
4198 /* Register/unregister the exmolobj Ruby object */
4200 MoleculeReleaseExternalObj(Molecule *mol)
4202 if (mol != NULL && mol->base.refCount <= 2 && mol->exmolobjProtected == 1) {
4203 rb_ary_delete(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4204 mol->exmolobjProtected = 0;
4209 MoleculeRetainExternalObj(Molecule *mol)
4211 if (mol != NULL && mol->base.refCount >= 2 && mol->exmolobj != NULL && mol->exmolobjProtected == 0) {
4212 if (sMoleculeRetainArray == Qnil) {
4213 rb_define_readonly_variable("molecules", &sMoleculeRetainArray);
4214 sMoleculeRetainArray = rb_ary_new();
4217 rb_ary_push(sMoleculeRetainArray, (VALUE)mol->exmolobj);
4218 mol->exmolobjProtected = 1;
4222 /* Release hook function for Ruby */
4224 MoleculeReleaseHook(Molecule *mol)
4226 if (mol->exmolobj != NULL) {
4227 /* No need to remove from sMoleculeRetainArray */
4228 mol->exmolobj = NULL;
4229 mol->exmolobjProtected = 0;
4231 MoleculeRelease(mol);
4235 ValueFromMolecule(Molecule *mol)
4239 if (mol->exmolobj != NULL)
4240 return (VALUE)mol->exmolobj;
4241 mol->exmolobj = (void *)Data_Wrap_Struct(rb_cMolecule, 0, (void (*)(void *))MoleculeReleaseHook, mol);
4242 MoleculeRetain(mol); /* MoleculeRetainExternalObj() is automatically called */
4243 return (VALUE)mol->exmolobj;
4248 s_Molecule_Alloc(VALUE klass)
4251 Molecule *mol = MoleculeNew();
4252 val = ValueFromMolecule(mol);
4253 MoleculeRelease(mol); /* Only the returned Ruby object retains this molecule */
4258 s_Molecule_AtomOrPiAtomIndexFromValue(Molecule *mol, VALUE val)
4262 if (FIXNUM_P(val)) {
4264 if (n >= 0 && n < mol->natoms)
4266 else if (n < 0 && -n - 1 < mol->npiatoms)
4267 return ATOMS_MAX_NUMBER + (-n - 1); /* PiAtom */
4268 n = -1; /* No such atom */
4269 val = rb_inspect(val);
4271 n = MoleculeAtomIndexFromString(mol, StringValuePtr(val));
4273 if (n >= 0 && n < mol->natoms)
4275 p = StringValuePtr(val);
4276 for (i = 0; i < mol->npiatoms; i++) {
4277 if (strcmp(p, mol->piatoms->aname) == 0)
4278 return ATOMS_MAX_NUMBER + i; /* PiAtom; should be looked up after normal atoms */
4281 rb_raise(rb_eMolbyError, "no such atom: %s", p);
4283 rb_raise(rb_eMolbyError, "bad format of atom specification: %s", p);
4285 rb_raise(rb_eMolbyError, "error in converting value to atom index: %s", p);
4286 return 0; /* Not reached */
4290 s_Molecule_AtomIndexFromValue(Molecule *mol, VALUE val)
4292 int n = s_Molecule_AtomOrPiAtomIndexFromValue(mol, val);
4293 if (n >= ATOMS_MAX_NUMBER) {
4294 val = rb_inspect(val);
4295 rb_raise(rb_eMolbyError, "no such atom: %s", StringValuePtr(val));
4301 s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
4304 val = rb_funcall(self, rb_intern("atom_group"), 1, val);
4305 if (!rb_obj_is_kind_of(val, rb_cIntGroup))
4306 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
4307 Data_Get_Struct(val, IntGroup, ig);
4316 * Duplicate a molecule. All entries are deep copied, so modifying the newly
4317 * created object does not affect the old object in any sense.
4320 s_Molecule_InitCopy(VALUE self, VALUE arg)
4322 Molecule *mp1, *mp2;
4323 Data_Get_Struct(self, Molecule, mp1);
4324 mp2 = MoleculeFromValue(arg);
4325 if (MoleculeInitWithMolecule(mp1, mp2) == NULL)
4326 rb_raise(rb_eMolbyError, "Cannot duplicate molecule");
4332 * loadmbsf(file) -> bool
4334 * Read a structure from a mbsf file.
4335 * Return true if successful.
4338 s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
4345 Data_Get_Struct(self, Molecule, mol);
4346 rb_scan_args(argc, argv, "1", &fname);
4347 fstr = FileStringValuePtr(fname);
4348 retval = MoleculeLoadMbsfFile(mol, fstr, errbuf, sizeof errbuf);
4350 /* if (retval == -1)
4352 rb_raise(rb_eMolbyError, errbuf);
4359 * loadpsf(file, pdbfile = nil) -> bool
4361 * Read a structure from a psf file. molecule must be empty. The psf may be
4362 * an "extended" version, which also contains coordinates. If pdbfile
4363 * is given, then atomic coordinates are read from that file.
4364 * Return true if successful.
4367 s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
4369 VALUE fname, pdbname;
4370 char *fstr, *pdbstr;
4374 Data_Get_Struct(self, Molecule, mol);
4375 if (mol->natoms > 0)
4376 return Qnil; /* Must be a new molecule */
4377 rb_scan_args(argc, argv, "11", &fname, &pdbname);
4378 fstr = FileStringValuePtr(fname);
4379 retval = MoleculeLoadPsfFile(mol, fstr, errbuf, sizeof errbuf);
4381 /* if (retval == -1)
4383 rb_raise(rb_eMolbyError, errbuf);
4386 if (!NIL_P(pdbname)) {
4387 pdbstr = strdup(FileStringValuePtr(pdbname));
4389 2008.7.19. The pdbfile should be explicitly given
4391 int len = strlen(fstr);
4392 if (len > 4 && strcmp(fstr + len - 4, ".psf") == 0) {
4393 pdbstr = ALLOC_N(char, len + 1);
4394 strcpy(pdbstr, fstr);
4395 strcpy(pdbstr + len - 4, ".pdb");
4398 if (pdbstr != NULL) {
4400 retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, errbuf, sizeof errbuf);
4402 /* if (retval == -1 && NIL_P(pdbname))
4407 rb_raise(rb_eMolbyError, errbuf);
4414 * loadpdb(file) -> bool
4416 * Read coordinates from a pdb file. If molecule is empty, then structure is build
4417 * by use of CONECT instructions. Otherwise, only the coordinates are read in.
4418 * Return true if successful.
4421 s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
4428 Data_Get_Struct(self, Molecule, mol);
4429 rb_scan_args(argc, argv, "1", &fname);
4430 fstr = FileStringValuePtr(fname);
4431 retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, errbuf, sizeof errbuf);
4433 /* if (retval == -1)
4435 rb_raise(rb_eMolbyError, errbuf);
4442 * loaddcd(file) -> bool
4444 * Read coordinates from a dcd file. The molecule should not empty.
4445 * Return true if successful.
4448 s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
4455 Data_Get_Struct(self, Molecule, mol);
4456 rb_scan_args(argc, argv, "1", &fname);
4457 fstr = FileStringValuePtr(fname);
4458 retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, errbuf, sizeof errbuf);
4460 /* if (retval == -1)
4462 rb_raise(rb_eMolbyError, errbuf);
4469 * loadtep(file) -> bool
4471 * Read coordinates from an ortep .tep file.
4472 * Return true if successful.
4475 s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
4482 Data_Get_Struct(self, Molecule, mol);
4483 rb_scan_args(argc, argv, "1", &fname);
4484 fstr = FileStringValuePtr(fname);
4485 retval = MoleculeLoadTepFile(mol, fstr, errbuf, sizeof errbuf);
4487 /* if (retval == -1)
4489 rb_raise(rb_eMolbyError, errbuf);
4496 * loadres(file) -> bool
4498 * Read coordinates from a shelx .res file.
4499 * Return true if successful.
4502 s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
4509 Data_Get_Struct(self, Molecule, mol);
4510 rb_scan_args(argc, argv, "1", &fname);
4511 fstr = FileStringValuePtr(fname);
4512 retval = MoleculeLoadShelxFile(mol, fstr, errbuf, sizeof errbuf);
4514 /* if (retval == -1)
4516 rb_raise(rb_eMolbyError, errbuf);
4523 * loadfchk(file) -> bool
4525 * Read coordinates and MO information from a Gaussian fchk file. (TODO: implement this)
4526 * Return true if successful.
4529 s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
4536 Data_Get_Struct(self, Molecule, mol);
4537 rb_scan_args(argc, argv, "1", &fname);
4538 fstr = FileStringValuePtr(fname);
4539 retval = MoleculeLoadGaussianFchkFile(mol, fstr, errbuf, sizeof errbuf);
4541 /* if (retval == -1)
4543 rb_raise(rb_eMolbyError, errbuf);
4550 * loaddat(file) -> bool
4552 * Read coordinates and ESP information from a GAMESS dat file. (TODO: read MO info as well)
4553 * Return true if successful.
4556 s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
4563 Data_Get_Struct(self, Molecule, mol);
4564 rb_scan_args(argc, argv, "1", &fname);
4565 fstr = FileStringValuePtr(fname);
4566 MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
4567 retval = MoleculeLoadGamessDatFile(mol, fstr, errbuf, sizeof errbuf);
4568 MyAppCallback_hideProgressPanel();
4570 /* if (retval == -1)
4572 rb_raise(rb_eMolbyError, errbuf);
4579 * savembsf(file) -> bool
4581 * Write structure as a mbsf file. Returns true if successful.
4584 s_Molecule_Savembsf(VALUE self, VALUE fname)
4589 Data_Get_Struct(self, Molecule, mol);
4590 fstr = FileStringValuePtr(fname);
4591 if (MoleculeWriteToMbsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4592 rb_raise(rb_eMolbyError, errbuf);
4598 * savepsf(file) -> bool
4600 * Write structure as a psf file. Returns true if successful.
4603 s_Molecule_Savepsf(VALUE self, VALUE fname)
4608 Data_Get_Struct(self, Molecule, mol);
4609 fstr = FileStringValuePtr(fname);
4610 if (MoleculeWriteToPsfFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4611 rb_raise(rb_eMolbyError, errbuf);
4617 * savepdb(file) -> bool
4619 * Write coordinates as a pdb file. Returns true if successful.
4622 s_Molecule_Savepdb(VALUE self, VALUE fname)
4627 Data_Get_Struct(self, Molecule, mol);
4628 fstr = FileStringValuePtr(fname);
4629 if (MoleculeWriteToPdbFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4630 rb_raise(rb_eMolbyError, errbuf);
4636 * savedcd(file) -> bool
4638 * Write coordinates as a dcd file. Returns true if successful.
4641 s_Molecule_Savedcd(VALUE self, VALUE fname)
4646 Data_Get_Struct(self, Molecule, mol);
4647 fstr = FileStringValuePtr(fname);
4648 if (MoleculeWriteToDcdFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4649 rb_raise(rb_eMolbyError, errbuf);
4655 * savetep(file) -> bool
4657 * Write coordinates as an ORTEP file. Returns true if successful.
4661 s_Molecule_Savetep(VALUE self, VALUE fname)
4666 Data_Get_Struct(self, Molecule, mol);
4667 fstr = FileStringValuePtr(fname);
4668 if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
4669 rb_raise(rb_eMolbyError, errbuf);
4674 /* load([ftype, ] fname, ...) */
4676 s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
4679 char *argstr, *methname, *p;
4682 const char *ls = (loadFlag ? "load" : "save");
4683 int lslen = strlen(ls);
4688 if (argc == 0 || (argstr = StringValuePtr(argv[0])) == NULL)
4689 rb_raise(rb_eMolbyError, "the first argument must be either filename or \":filetype\"");
4690 if (argstr[0] == ':') {
4691 /* Call "loadXXX" (or "saveXXX") for type ":XXX" */
4692 methname = ALLOC_N(char, lslen + strlen(argstr));
4693 strcpy(methname, ls);
4694 strcat(methname, argstr + 1);
4695 for (i = lslen; methname[i] != 0; i++)
4696 methname[i] = tolower(methname[i]);
4697 mid = rb_intern(methname);
4701 rval = rb_funcall2(self, mid, argc, argv);
4703 rb_raise(rb_eMolbyError, "the format specification \'%s\' seems to be wrong", argstr);
4707 /* Guess file type from extension */
4708 p = strrchr(argstr, '.');
4711 for (methname = p; *methname != 0; methname++) {
4712 if (!isalpha(*methname))
4715 if (*methname == 0) {
4716 methname = ALLOC_N(char, lslen + strlen(p) + 1);
4717 if (methname == NULL)
4718 rb_raise(rb_eMolbyError, "Low memory");
4719 strcpy(methname, ls);
4720 strcat(methname, p);
4721 for (i = lslen; methname[i] != 0; i++)
4722 methname[i] = tolower(methname[i]);
4723 mid = rb_intern(methname);
4726 if (rb_respond_to(self, mid)) {
4727 /* Load: try to call the load procedure only if it is available */
4728 rval = rb_funcall2(self, mid, argc, argv);
4733 /* Save: call the save procedure, and if not found then call 'method_missing' */
4734 rval = rb_funcall2(self, mid, argc, argv);
4740 rb_raise(rb_eMolbyError, "the file %s cannot be %s", argstr, (loadFlag ? "loaded" : "saved"));
4744 /* Register the path */
4747 Data_Get_Struct(self, Molecule, mol);
4748 MoleculeSetPath(mol, StringValuePtr(argv[0]));
4750 /* Check if all occupancy factors are zero; if that is the case, then all set to 1.0 */
4751 /* for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4752 if (ap->occupancy != 0.0)
4755 if (i == mol->natoms) {
4756 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
4757 ap->occupancy = 1.0;
4766 * molload(file, *args) -> bool
4768 * Read a structure from the given file by calling the public method "loadXXX" (XXX is the
4769 * file type given by the extension). If this method fails, then all defined (public)
4770 * "loadXXX" methods are invoked, and raises an exception if none of them were successful.
4773 s_Molecule_Load(int argc, VALUE *argv, VALUE self)
4775 return s_Molecule_LoadSave(argc, argv, self, 1);
4780 * molsave(file, *args) -> bool
4782 * Write a structure/coordinate to the given file by calling the public method "saveXXX"
4783 * (XXX is the file type given by the extension).
4786 s_Molecule_Save(int argc, VALUE *argv, VALUE self)
4788 return s_Molecule_LoadSave(argc, argv, self, 0);
4795 * Returns the display name of the molecule. If the molecule has no associated
4796 * document, then returns nil.
4799 s_Molecule_Name(VALUE self)
4803 Data_Get_Struct(self, Molecule, mol);
4804 MoleculeCallback_displayName(mol, buf, sizeof buf);
4808 return rb_str_new2(buf);
4813 * set_name(string) -> self
4815 * Set the name of an untitled molecule. If the molecule is not associated with window
4816 * or it already has an associated file, then exception is thrown.
4819 s_Molecule_SetName(VALUE self, VALUE nval)
4822 Data_Get_Struct(self, Molecule, mol);
4823 if (MoleculeCallback_setDisplayName(mol, StringValuePtr(nval)))
4824 rb_raise(rb_eMolbyError, "Cannot change the window title");
4833 * Returns the full path name of the molecule, if it is associated with a file.
4834 * If the molecule has no associated file, then returns nil.
4837 s_Molecule_Path(VALUE self)
4841 Data_Get_Struct(self, Molecule, mol);
4842 MoleculeCallback_pathName(mol, buf, sizeof buf);
4846 return Ruby_NewFileStringValue(buf);
4853 * Returns the full path name of the directory in which the file associated with the
4854 * molecule is located. If the molecule has no associated file, then returns nil.
4857 s_Molecule_Dir(VALUE self)
4861 Data_Get_Struct(self, Molecule, mol);
4862 MoleculeCallback_pathName(mol, buf, sizeof buf);
4864 translate_char(buf, '\\', '/');
4869 p = strrchr(buf, '/');
4872 return rb_str_new2(buf);
4880 * Returns a string in the form "Molecule[name]" if the molecule has the associated
4881 * document. Otherwise, a string "<Molecule:0x****>" (the address is the address of
4882 * the Molecule structure) is returned.
4885 s_Molecule_Inspect(VALUE self)
4889 Data_Get_Struct(self, Molecule, mol);
4890 MoleculeCallback_displayName(mol, buf, sizeof buf);
4892 /* No associated document */
4893 snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
4894 return rb_str_new2(buf);
4896 /* Check whether the document name is duplicate */
4900 for (idx = 0, k = k2 = 0; (mol2 = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
4901 MoleculeCallback_displayName(mol2, buf2, sizeof buf2);
4902 if (strcmp(buf, buf2) == 0) {
4909 snprintf(buf2, sizeof buf2, "Molecule[\"%s\",%d]", buf, k2);
4911 snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
4913 return rb_str_new2(buf2);
4920 * open(file) -> Molecule
4922 * Create a new molecule from file as a document. If file is not given, an untitled document is created.
4925 s_Molecule_Open(int argc, VALUE *argv, VALUE self)
4931 rb_scan_args(argc, argv, "01", &fname);
4935 p = FileStringValuePtr(fname);
4936 iflag = Ruby_SetInterruptFlag(Qfalse);
4937 mp = MoleculeCallback_openNewMolecule(p);
4938 Ruby_SetInterruptFlag(iflag);
4941 rb_raise(rb_eMolbyError, "Cannot create untitled document");
4943 rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
4945 return ValueFromMolecule(mp);
4951 * new(file, *args) -> Molecule
4953 * Create a new molecule and call "load" method with the same arguments.
4956 s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
4959 return s_Molecule_Load(argc, argv, self);
4960 else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
4964 s_Molecule_MolEnumerable(VALUE self, int kind)
4967 MolEnumerable *mseq;
4968 Data_Get_Struct(self, Molecule, mol);
4969 mseq = MolEnumerableNew(mol, kind);
4970 return Data_Wrap_Struct(rb_cMolEnumerable, 0, (void (*)(void *))MolEnumerableRelease, mseq);
4975 * atoms -> MolEnumerable
4977 * Returns a MolEnumerable object representing the array of atoms.
4980 s_Molecule_Atoms(VALUE self)
4982 return s_Molecule_MolEnumerable(self, kAtomKind);
4987 * bonds -> MolEnumerable
4989 * Returns a MolEnumerable object representing the array of bonds. A bond is represented
4990 * by an array of two atom indices.
4993 s_Molecule_Bonds(VALUE self)
4995 return s_Molecule_MolEnumerable(self, kBondKind);
5000 * angles -> MolEnumerable
5002 * Returns a MolEnumerable object representing the array of angles. An angle is represented
5003 * by an array of three atom indices.
5006 s_Molecule_Angles(VALUE self)
5008 return s_Molecule_MolEnumerable(self, kAngleKind);
5013 * dihedrals -> MolEnumerable
5015 * Returns a MolEnumerable object representing the array of dihedrals. A dihedral is represented
5016 * by an array of four atom indices.
5019 s_Molecule_Dihedrals(VALUE self)
5021 return s_Molecule_MolEnumerable(self, kDihedralKind);
5026 * impropers -> MolEnumerable
5028 * Returns a MolEnumerable object representing the array of impropers. An improper is represented
5029 * by an array of four atom indices.
5032 s_Molecule_Impropers(VALUE self)
5034 return s_Molecule_MolEnumerable(self, kImproperKind);
5039 * residues -> MolEnumerable
5041 * Returns a MolEnumerable object representing the array of residue names.
5044 s_Molecule_Residues(VALUE self)
5046 return s_Molecule_MolEnumerable(self, kResidueKind);
5053 * Returns the number of atoms.
5056 s_Molecule_Natoms(VALUE self)
5059 Data_Get_Struct(self, Molecule, mol);
5060 return INT2NUM(mol->natoms);
5067 * Returns the number of bonds.
5070 s_Molecule_Nbonds(VALUE self)
5073 Data_Get_Struct(self, Molecule, mol);
5074 return INT2NUM(mol->nbonds);
5079 * nangles -> Integer
5081 * Returns the number of angles.
5084 s_Molecule_Nangles(VALUE self)
5087 Data_Get_Struct(self, Molecule, mol);
5088 return INT2NUM(mol->nangles);
5093 * ndihedrals -> Integer
5095 * Returns the number of dihedrals.
5098 s_Molecule_Ndihedrals(VALUE self)
5101 Data_Get_Struct(self, Molecule, mol);
5102 return INT2NUM(mol->ndihedrals);
5107 * nimpropers -> Integer
5109 * Returns the number of impropers.
5112 s_Molecule_Nimpropers(VALUE self)
5115 Data_Get_Struct(self, Molecule, mol);
5116 return INT2NUM(mol->nimpropers);
5121 * nresidues -> Integer
5123 * Returns the number of residues.
5126 s_Molecule_Nresidues(VALUE self)
5129 Data_Get_Struct(self, Molecule, mol);
5130 return INT2NUM(mol->nresidues);
5134 s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
5136 rb_raise(rb_eMolbyError, "Molecule#bond_par, angle_par, dihedral_par, improper_par, vdw_par are now obsolete. You can use MDArena#bond_par, angle_par, dihedral_par, improper_par, vdw_par instead, and probably these are what you really want.");
5141 * bond_par(idx) -> ParameterRef
5143 * Returns the MD parameter for the idx-th bond.
5147 s_Molecule_BondPar(VALUE self, VALUE val)
5154 Data_Get_Struct(self, Molecule, mol);
5155 ival = NUM2INT(rb_Integer(val));
5156 if (ival < -mol->nbonds || ival >= mol->nbonds)
5157 rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
5159 ival += mol->nbonds;
5160 s_RebuildMDParameterIfNecessary(self, Qtrue);
5161 i1 = mol->bonds[ival * 2];
5162 i2 = mol->bonds[ival * 2 + 1];
5163 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5164 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5165 bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
5168 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
5174 * angle_par(idx) -> ParameterRef
5176 * Returns the MD parameter for the idx-th angle.
5180 s_Molecule_AnglePar(VALUE self, VALUE val)
5187 Data_Get_Struct(self, Molecule, mol);
5188 ival = NUM2INT(rb_Integer(val));
5189 if (ival < -mol->nangles || ival >= mol->nangles)
5190 rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
5192 ival += mol->nangles;
5193 s_RebuildMDParameterIfNecessary(self, Qtrue);
5194 i1 = mol->angles[ival * 3];
5195 i2 = mol->angles[ival * 3 + 1];
5196 i3 = mol->angles[ival * 3 + 2];
5197 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5198 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5199 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5200 ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
5203 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
5208 * dihedral_par(idx) -> ParameterRef
5210 * Returns the MD parameter for the idx-th dihedral.
5214 s_Molecule_DihedralPar(VALUE self, VALUE val)
5219 UInt t1, t2, t3, t4;
5221 Data_Get_Struct(self, Molecule, mol);
5222 ival = NUM2INT(rb_Integer(val));
5223 if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
5224 rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
5226 ival += mol->ndihedrals;
5227 s_RebuildMDParameterIfNecessary(self, Qtrue);
5228 i1 = mol->dihedrals[ival * 4];
5229 i2 = mol->dihedrals[ival * 4 + 1];
5230 i3 = mol->dihedrals[ival * 4 + 2];
5231 i4 = mol->dihedrals[ival * 4 + 3];
5232 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5233 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5234 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5235 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5236 tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5239 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
5244 * improper_par(idx) -> ParameterRef
5246 * Returns the MD parameter for the idx-th improper.
5250 s_Molecule_ImproperPar(VALUE self, VALUE val)
5255 UInt t1, t2, t3, t4;
5257 Data_Get_Struct(self, Molecule, mol);
5258 ival = NUM2INT(rb_Integer(val));
5259 if (ival < -mol->nimpropers || ival >= mol->nimpropers)
5260 rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
5262 ival += mol->nimpropers;
5263 s_RebuildMDParameterIfNecessary(self, Qtrue);
5264 i1 = mol->impropers[ival * 4];
5265 i2 = mol->impropers[ival * 4 + 1];
5266 i3 = mol->impropers[ival * 4 + 2];
5267 i4 = mol->impropers[ival * 4 + 3];
5268 t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
5269 t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
5270 t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
5271 t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
5272 tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
5275 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
5280 * vdw_par(idx) -> ParameterRef
5282 * Returns the vdw parameter for the idx-th atom.
5286 s_Molecule_VdwPar(VALUE self, VALUE val)
5292 Data_Get_Struct(self, Molecule, mol);
5293 ival = NUM2INT(rb_Integer(val));
5294 if (ival < -mol->natoms || ival >= mol->natoms)
5295 rb_raise(rb_eMolbyError, "atom index (%d) out of range", ival);
5297 ival += mol->natoms;
5298 s_RebuildMDParameterIfNecessary(self, Qtrue);
5299 t1 = ATOM_AT_INDEX(mol->atoms, ival)->type;
5300 vp = ParameterLookupVdwPar(mol->par, t1, 0);
5303 return ValueFromMoleculeWithParameterTypeAndIndex(mol, kVdwParType, vp - mol->par->vdwPars);
5309 * start_step -> Integer
5311 * Returns the start step (defined by dcd format).
5314 s_Molecule_StartStep(VALUE self)
5317 Data_Get_Struct(self, Molecule, mol);
5318 return INT2NUM(mol->startStep);
5323 * start_step = Integer
5325 * Set the start step (defined by dcd format).
5328 s_Molecule_SetStartStep(VALUE self, VALUE val)
5331 Data_Get_Struct(self, Molecule, mol);
5332 mol->startStep = NUM2INT(rb_Integer(val));
5338 * steps_per_frame -> Integer
5340 * Returns the number of steps between frames (defined by dcd format).
5343 s_Molecule_StepsPerFrame(VALUE self)
5346 Data_Get_Struct(self, Molecule, mol);
5347 return INT2NUM(mol->stepsPerFrame);
5352 * steps_per_frame = Integer
5354 * Set the number of steps between frames (defined by dcd format).
5357 s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
5360 Data_Get_Struct(self, Molecule, mol);
5361 mol->stepsPerFrame = NUM2INT(rb_Integer(val));
5367 * ps_per_step -> Float
5369 * Returns the time increment (in picoseconds) for one step (defined by dcd format).
5372 s_Molecule_PsPerStep(VALUE self)
5375 Data_Get_Struct(self, Molecule, mol);
5376 return rb_float_new(mol->psPerStep);
5381 * ps_per_step = Float
5383 * Set the time increment (in picoseconds) for one step (defined by dcd format).
5386 s_Molecule_SetPsPerStep(VALUE self, VALUE val)
5389 Data_Get_Struct(self, Molecule, mol);
5390 mol->psPerStep = NUM2DBL(rb_Float(val));
5396 * find_angles -> Integer
5398 * Find the angles from the bonds. Returns the number of angles newly created.
5402 s_Molecule_FindAngles(VALUE self)
5408 Data_Get_Struct(self, Molecule, mol);
5409 if (mol == NULL || mol->natoms == 0)
5413 for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
5414 nc = ap->connect.count;
5416 for (i = 0; i < nc; i++) {
5417 n[0] = ap->connects[i];
5418 for (j = i + 1; j < nc; j++) {
5419 n[2] = ap->connects[j];
5420 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
5421 AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
5426 MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
5429 return INT2NUM(nip);
5434 * find_dihedrals -> Integer
5436 * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
5440 s_Molecule_FindDihedrals(VALUE self)
5444 int n1, i, j, k, nc1, nc2;
5446 Data_Get_Struct(self, Molecule, mol);
5447 if (mol == NULL || mol->natoms == 0)
5451 for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
5452 nc1 = ap1->connect.count;
5454 for (i = 0; i < nc1; i++) {
5455 n[2] = ap1->connects[i];
5458 ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
5459 nc2 = ap2->connect.count;
5460 for (j = 0; j < nc1; j++) {
5461 n[0] = ap1->connects[j];
5464 for (k = 0; k < nc2; k++) {
5465 n[3] = ap2->connects[k];
5466 if (n[3] == n1 || n[3] == n[0])
5468 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
5469 AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
5475 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
5478 return INT2NUM(nip);
5484 * nresidues = Integer
5486 * Change the number of residues.
5489 s_Molecule_ChangeNresidues(VALUE self, VALUE val)
5492 int ival = NUM2INT(val);
5493 Data_Get_Struct(self, Molecule, mol);
5494 MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
5495 if (ival != mol->nresidues)
5496 rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
5502 * max_residue_number(atom_group = nil) -> Integer
5504 * Returns the maximum residue number actually used. If an atom group is given, only
5505 * these atoms are examined. If no atom is present, nil is returned.
5508 s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
5514 Data_Get_Struct(self, Molecule, mol);
5515 rb_scan_args(argc, argv, "01", &gval);
5516 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5517 maxSeq = MoleculeMaximumResidueNumber(mol, ig);
5518 return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
5523 * min_residue_number(atom_group = nil) -> Integer
5525 * Returns the minimum residue number actually used. If an atom group is given, only
5526 * these atoms are examined. If no atom is present, nil is returned.
5529 s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
5535 Data_Get_Struct(self, Molecule, mol);
5536 rb_scan_args(argc, argv, "01", &gval);
5537 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5538 minSeq = MoleculeMinimumResidueNumber(mol, ig);
5539 return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
5544 * each_atom(atom_group = nil) {|aref| ...}
5546 * Execute the block, with the AtomRef object for each atom as the argument. If an atom
5547 * group is given, only these atoms are processed.
5548 * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
5549 * is self (a Molecule object).
5552 s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
5560 Data_Get_Struct(self, Molecule, mol);
5561 rb_scan_args(argc, argv, "01", &gval);
5562 ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
5563 arval = ValueFromMoleculeAndIndex(mol, 0);
5564 Data_Get_Struct(arval, AtomRef, aref);
5565 for (i = 0; i < mol->natoms; i++) {
5567 if (ig == NULL || IntGroupLookup(ig, i, NULL))
5571 IntGroupRelease(ig);
5577 * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5579 * Returns the unit cell parameters. If cell is not set, returns nil.
5582 s_Molecule_Cell(VALUE self)
5587 Data_Get_Struct(self, Molecule, mol);
5588 if (mol->cell == NULL)
5590 val = rb_ary_new2(6);
5591 for (i = 0; i < 6; i++)
5592 rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
5593 if (mol->cell->has_sigma) {
5594 for (i = 0; i < 6; i++) {
5595 rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
5603 * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
5604 * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], flag = nil)
5606 * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
5607 If the given argument has 12 members, then the second half of the parameters represents the sigma values.
5608 This operation is undoable. If the second argument is given as non-nil, then
5609 the coordinates are transformed so that the fractional coordinates remain the same.
5612 s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
5618 Data_Get_Struct(self, Molecule, mol);
5619 rb_scan_args(argc, argv, "11", &val, &fval);
5622 MolActionCreateAndPerform(mol, gMolActionSetCell, 0, d, flag);
5623 // MoleculeSetCell(mol, 1, 1, 1, 90, 90, 90);
5626 val = rb_ary_to_ary(val);
5627 if (RARRAY_LEN(val) >= 12) {
5629 } else if (RARRAY_LEN(val) >= 6) {
5631 } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
5632 for (i = 0; i < n; i++)
5633 d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
5634 MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, flag);
5640 * cell_transform -> Transform
5642 * Get the transform matrix that converts internal coordinates to cartesian coordinates.
5643 * If cell is not defined, nil is returned.
5646 s_Molecule_CellTransform(VALUE self)
5649 Data_Get_Struct(self, Molecule, mol);
5650 if (mol == NULL || mol->cell == NULL)
5652 return ValueFromTransform(&(mol->cell->tr));
5657 * box -> [avec, bvec, cvec, origin, flags]
5659 * Get the unit cell information in the form of a periodic bounding box.
5660 * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
5661 * Integers which define whether the system is periodic along the axis.
5662 * If no unit cell is defined, nil is returned.
5665 s_Molecule_Box(VALUE self)
5669 Data_Get_Struct(self, Molecule, mol);
5670 if (mol == NULL || mol->cell == NULL)
5672 v[0] = ValueFromVector(&(mol->cell->axes[0]));
5673 v[1] = ValueFromVector(&(mol->cell->axes[1]));
5674 v[2] = ValueFromVector(&(mol->cell->axes[2]));
5675 v[3] = ValueFromVector(&(mol->cell->origin));
5676 v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
5677 val = rb_ary_new4(5, v);
5683 * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
5684 * set_box(d, origin = [0, 0, 0])
5687 * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
5688 If it is a number, the x/y/z axis vector is multiplied with the given number and used
5690 Flags, if present, is a 3-member array of Integers defining whether the system is
5691 periodic along the axis.
5692 If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
5693 In the second form, an isotropic box with cell-length d is set.
5694 In the third form, the existing box is cleared.
5695 Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
5698 s_Molecule_SetBox(VALUE self, VALUE aval)
5702 static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
5704 Vector origin = {0, 0, 0};
5707 int i, convertCoordinates = 0;
5708 Data_Get_Struct(self, Molecule, mol);
5709 for (i = 0; i < 6; i++) {
5710 if (i < RARRAY_LEN(aval))
5711 v[i] = (RARRAY_PTR(aval))[i];
5715 MolActionCreateAndPerform(mol, gMolActionClearBox);
5718 if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
5719 d = NUM2DBL(rb_Float(v[0]));
5720 for (i = 0; i < 3; i++)
5721 VecScale(vv[i], ax[i], d);
5723 VectorFromValue(v[1], &origin);
5724 flags[0] = flags[1] = flags[2] = 1;
5726 for (i = 0; i < 3; i++) {
5729 } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
5730 d = NUM2DBL(rb_Float(v[i]));
5731 VecScale(vv[i], ax[i], d);
5733 VectorFromValue(v[i], &vv[i]);
5735 flags[i] = (VecLength2(vv[i]) > 0.0);
5738 VectorFromValue(v[3], &origin);
5740 for (i = 0; i < 3; i++) {
5741 VALUE val = Ruby_ObjectAtIndex(v[4], i);
5742 flags[i] = (NUM2INT(rb_Integer(val)) != 0);
5746 convertCoordinates = 1;
5748 MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
5754 * cell_flexibility -> bool
5756 * Returns the unit cell is flexible or not
5759 s_Molecule_CellFlexibility(VALUE self)
5762 Data_Get_Struct(self, Molecule, mol);
5763 if (mol->cell == NULL)
5765 if (mol->useFlexibleCell)
5772 * self.cell_flexibility = bool
5773 * set_cell_flexibility(bool)
5775 * Change the unit cell is flexible or not
5778 s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
5781 Data_Get_Struct(self, Molecule, mol);
5782 MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
5788 * symmetry -> Array of Transforms
5789 * symmetries -> Array of Transforms
5791 * Get the currently defined symmetry operations. If no symmetry operation is defined,
5792 * returns an empty array.
5795 s_Molecule_Symmetry(VALUE self)
5800 Data_Get_Struct(self, Molecule, mol);
5801 if (mol->nsyms <= 0)
5802 return rb_ary_new();
5803 val = rb_ary_new2(mol->nsyms);
5804 for (i = 0; i < mol->nsyms; i++) {
5805 rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
5812 * nsymmetries -> Integer
5814 * Get the number of currently defined symmetry operations.
5817 s_Molecule_Nsymmetries(VALUE self)
5820 Data_Get_Struct(self, Molecule, mol);
5821 return INT2NUM(mol->nsyms);
5826 * add_symmetry(Transform) -> Integer
5828 * Add a new symmetry operation. If no symmetry operation is defined and the
5829 * given argument is not an identity transform, then also add an identity
5830 * transform at the index 0.
5831 * Returns the total number of symmetries after operation.
5834 s_Molecule_AddSymmetry(VALUE self, VALUE trans)
5838 Data_Get_Struct(self, Molecule, mol);
5839 TransformFromValue(trans, &tr);
5840 MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
5841 return INT2NUM(mol->nsyms);
5846 * remove_symmetry(count = nil) -> Integer
5847 * remove_symmetries(count = nil) -> Integer
5849 * Remove the specified number of symmetry operations. The last added ones are removed
5850 * first. If count is nil, then all symmetry operations are removed. Returns the
5851 * number of leftover symmetries.
5854 s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
5859 Data_Get_Struct(self, Molecule, mol);
5860 rb_scan_args(argc, argv, "01", &cval);
5864 n = NUM2INT(rb_Integer(cval));
5865 if (n < 0 || n > mol->nsyms)
5866 rb_raise(rb_eMolbyError, "the given count of symops is out of range");
5867 if (n == mol->nsyms)
5870 for (i = 0; i < n; i++)
5871 MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
5872 return INT2NUM(mol->nsyms);
5876 s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
5878 Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
5879 IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
5880 int idx = s_Molecule_AtomIndexFromValue(mol, arg);
5881 IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
5888 * atom_group {|aref| ...}
5889 * atom_group(arg1, arg2, ...)
5890 * atom_group(arg1, arg2, ...) {|aref| ...}
5892 * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
5893 * If arguments are given, then the atoms reprensented by the arguments are added to the
5894 * group. For a conversion of a string to an atom index, see the description
5895 * of Molecule#atom_index.
5896 * If a block is given, it is evaluated with an AtomRef (not atom index integers)
5897 * representing each atom, and the atoms are removed from the result if the block returns false.
5901 s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
5903 IntGroup *ig1, *ig2;
5905 Int i, startPt, interval;
5906 VALUE retval = IntGroup_Alloc(rb_cIntGroup);
5907 Data_Get_Struct(retval, IntGroup, ig1);
5908 Data_Get_Struct(self, Molecule, mol);
5910 IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
5913 if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
5914 i = s_Molecule_AtomIndexFromValue(mol, *argv);
5915 IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
5916 } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
5917 ig2 = IntGroupFromValue(*argv);
5918 for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
5919 interval = IntGroupGetInterval(ig2, i);
5920 IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
5922 IntGroupRelease(ig2);
5923 } else if (rb_respond_to(*argv, rb_intern("each"))) {
5925 values[0] = (VALUE)mol;
5926 values[1] = (VALUE)ig1;
5927 rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
5929 IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
5934 if (rb_block_given_p()) {
5935 /* Evaluate the given block with an AtomRef as the argument, and delete
5936 the index if the block returns false */
5937 AtomRef *aref = AtomRefNew(mol, 0);
5938 VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
5939 ig2 = IntGroupNew();
5940 IntGroupCopy(ig2, ig1);
5941 for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
5943 if (startPt >= mol->natoms)
5945 aref->idx = startPt;
5946 resval = rb_yield(arval);
5948 IntGroupRemove(ig1, startPt, 1);
5950 IntGroupRelease(ig2);
5953 /* Remove points that are out of bounds */
5954 IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
5961 * atom_index(val) -> Integer
5963 * Returns the atom index represented by val. val can be either a non-negative integer
5964 * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
5965 * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
5966 * where resname, resid, name are the residue name, residue id, and atom name respectively.
5967 * If val is a string and multiple atoms match the description, the atom with the lowest index
5971 s_Molecule_AtomIndex(VALUE self, VALUE val)
5974 Data_Get_Struct(self, Molecule, mol);
5975 return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
5980 * extract(group, dummy_flag = nil) -> Molecule
5982 * Extract the atoms given by group and return as a new molecule object.
5983 * If dummy_flag is true, then the atoms that are not included in the group but are connected
5984 * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
5985 * names beginning with an underscore) and included in the new molecule object.
5988 s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
5990 Molecule *mol1, *mol2;
5992 VALUE group, dummy_flag, retval;
5993 Data_Get_Struct(self, Molecule, mol1);
5994 rb_scan_args(argc, argv, "11", &group, &dummy_flag);
5995 ig = s_Molecule_AtomGroupFromValue(self, group);
5996 if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
5999 retval = ValueFromMolecule(mol2);
6001 IntGroupRelease(ig);
6007 * add(molecule2) -> self
6009 * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
6011 This operation is undoable.
6014 s_Molecule_Add(VALUE self, VALUE val)
6016 Molecule *mol1, *mol2;
6017 Data_Get_Struct(self, Molecule, mol1);
6018 mol2 = MoleculeFromValue(val);
6019 MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
6025 * remove(group) -> Molecule
6027 * The atoms designated by the given group are removed from the molecule.
6028 * This operation is undoable.
6031 s_Molecule_Remove(VALUE self, VALUE group)
6035 Int *bonds, nbonds, i, temp[2];
6036 IntGroupIterator iter;
6038 Data_Get_Struct(self, Molecule, mol1);
6039 group = rb_funcall(self, rb_intern("atom_group"), 1, group);
6040 if (!rb_obj_is_kind_of(group, rb_cIntGroup))
6041 rb_raise(rb_eMolbyError, "IntGroup instance is expected");
6042 Data_Get_Struct(group, IntGroup, ig);
6044 /* Remove the bonds between the two fragments */
6045 /* (This is necessary for undo to work correctly) */
6048 IntGroupIteratorInit(ig, &iter);
6049 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
6050 Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
6052 cp = AtomConnectData(&ap->connect);
6053 for (j = 0; j < ap->connect.count; j++) {
6055 if (!IntGroupLookup(ig, n, NULL)) {
6056 /* bond i-n, i is in ig and n is not */
6059 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp);
6063 IntGroupIteratorRelease(&iter);
6066 /* temp[0] = kInvalidIndex;
6067 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, temp); */
6068 MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, nbonds * 2, bonds);
6072 if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
6079 * create_atom(name, pos = -1) -> AtomRef
6081 * Create a new atom with the specified name (may contain residue
6082 * information) and position (if position is out of range, the atom is appended at
6083 * the end). Returns the reference to the new atom.
6084 * This operation is undoable.
6087 s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
6094 char *p, resName[6], atomName[6];
6096 Data_Get_Struct(self, Molecule, mol);
6097 rb_scan_args(argc, argv, "02", &name, &ival);
6099 pos = NUM2INT(rb_Integer(ival));
6102 p = StringValuePtr(name);
6104 i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
6105 if (atomName[0] == 0)
6106 rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
6109 if (p == NULL || p[0] == 0) {
6110 memset(atomName, 0, 4);
6113 memset(&arec, 0, sizeof(arec));
6114 strncpy(arec.aname, atomName, 4);
6116 strncpy(arec.resName, resName, 4);
6117 arec.resSeq = resSeq;
6119 arec.occupancy = 1.0;
6120 // i = MoleculeCreateAnAtom(mol, &arec);
6121 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6123 aref = AtomRefNew(mol, pos);
6124 return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6129 * duplicate_atom(atomref, pos = -1) -> AtomRef
6131 * Create a new atom with the same attributes (but no bonding information)
6132 * with the specified atom. Returns the reference to the new atom.
6133 * This operation is undoable.
6136 s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
6142 VALUE retval, aval, ival;
6144 Data_Get_Struct(self, Molecule, mol);
6145 rb_scan_args(argc, argv, "11", &aval, &ival);
6146 if (FIXNUM_P(aval)) {
6147 int idx = NUM2INT(aval);
6148 if (idx < 0 || idx >= mol->natoms)
6149 rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
6150 apsrc = ATOM_AT_INDEX(mol->atoms, idx);
6152 apsrc = s_AtomFromValue(aval);
6155 rb_raise(rb_eMolbyError, "bad atom specification");
6157 pos = NUM2INT(rb_Integer(ival));
6159 AtomDuplicate(&arec, apsrc);
6160 arec.connect.count = 0;
6161 if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
6164 aref = AtomRefNew(mol, pos);
6165 retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
6173 * create_bond(n1, n2, ...) -> Integer
6175 * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
6176 * do nothing for that pair. Returns the number of bonds actually created.
6177 * This operation is undoable.
6180 s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
6183 Int i, *ip, old_nbonds;
6185 rb_raise(rb_eMolbyError, "missing arguments");
6187 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6188 Data_Get_Struct(self, Molecule, mol);
6189 ip = ALLOC_N(Int, argc + 1);
6190 for (i = 0; i < argc; i++) {
6191 ip[i] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6193 ip[argc] = kInvalidIndex;
6194 old_nbonds = mol->nbonds;
6195 i = MolActionCreateAndPerform(mol, gMolActionAddBonds, argc, ip);
6198 rb_raise(rb_eMolbyError, "atom index out of range");
6200 rb_raise(rb_eMolbyError, "too many bonds");
6202 rb_raise(rb_eMolbyError, "duplicate bonds");
6204 rb_raise(rb_eMolbyError, "cannot create bond to itself");
6206 rb_raise(rb_eMolbyError, "error in creating bonds");
6207 return INT2NUM(mol->nbonds - old_nbonds);
6212 * molecule.remove_bonds(n1, n2, ...) -> Integer
6214 * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
6215 * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
6216 * This operation is undoable.
6219 s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
6222 Int i, *ip, old_nbonds;
6224 rb_raise(rb_eMolbyError, "missing arguments");
6226 rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
6227 Data_Get_Struct(self, Molecule, mol);
6228 ip = ALLOC_N(Int, argc + 1);
6229 for (i = 0; i < argc; i++)
6230 ip[i] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
6231 ip[argc] = kInvalidIndex;
6232 old_nbonds = mol->nbonds;
6233 MolActionCreateAndPerform(mol, gMolActionDeleteBonds, argc, ip);
6235 return INT2NUM(old_nbonds - mol->nbonds);
6240 * add_angle(n1, n2, n3) -> Molecule
6242 * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
6243 * when a bond is created, so it is rarely necessary to use this method explicitly.
6244 * This operation is undoable.
6247 s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6251 Data_Get_Struct(self, Molecule, mol);
6252 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6253 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6254 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6255 if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
6256 rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
6257 n[3] = kInvalidIndex;
6258 MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
6264 * remove_angle(n1, n2, n3) -> Molecule
6266 * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
6267 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6268 * This operation is undoable.
6271 s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
6276 Data_Get_Struct(self, Molecule, mol);
6277 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6278 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6279 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6280 if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
6281 rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
6282 ig = IntGroupNewWithPoints(n[3], 1, -1);
6283 MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
6284 IntGroupRelease(ig);
6290 * add_dihedral(n1, n2, n3, n4) -> Molecule
6292 * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
6293 * when a bond is created, so it is rarely necessary to use this method explicitly.
6294 * This operation is undoable.
6297 s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6301 Data_Get_Struct(self, Molecule, mol);
6302 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6303 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6304 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6305 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6306 if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
6307 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6308 n[4] = kInvalidIndex;
6309 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
6315 * remove_dihedral(n1, n2, n3, n4) -> Molecule
6317 * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
6318 * when a bond is removed, so it is rarely necessary to use this method explicitly.
6319 * This operation is undoable.
6322 s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6327 Data_Get_Struct(self, Molecule, mol);
6328 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6329 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6330 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6331 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6332 if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
6333 rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6334 ig = IntGroupNewWithPoints(n[4], 1, -1);
6335 MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
6336 IntGroupRelease(ig);
6342 * add_improper(n1, n2, n3, n4) -> Molecule
6344 * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6345 * not automatically added when a new bond is created, so this method is more useful than
6346 * the angle/dihedral counterpart.
6347 * This operation is undoable.
6350 s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6354 Data_Get_Struct(self, Molecule, mol);
6355 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6356 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6357 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6358 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6359 if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
6360 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
6361 n[4] = kInvalidIndex;
6362 MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
6368 * remove_improper(n1, n2, n3, n4) -> Molecule
6370 * Remove improper n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
6371 * not automatically added when a new bond is created, so this method is more useful than
6372 * the angle/dihedral counterpart.
6373 * This operation is undoable.
6376 s_Molecule_RemoveImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
6381 Data_Get_Struct(self, Molecule, mol);
6382 n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
6383 n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
6384 n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
6385 n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
6386 if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
6387 rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
6388 ig = IntGroupNewWithPoints(n[4], 1, -1);
6389 MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
6390 IntGroupRelease(ig);
6396 * assign_residue(group, res) -> Molecule
6398 * Assign the specified atoms as the given residue. res can either be an integer, "resname"
6399 * or "resname.resno". When the residue number is not specified, the residue number of
6400 * the first atom in the group is used.
6401 * This operation is undoable.
6404 s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
6408 char *p, *pp, buf[16];
6411 Data_Get_Struct(self, Molecule, mol);
6413 /* Parse the argument res */
6414 if (FIXNUM_P(res)) {
6415 /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
6416 resid = NUM2INT(res);
6419 p = StringValuePtr(res);
6420 pp = strchr(p, '.');
6422 resid = atoi(pp + 1);
6428 if (n > sizeof buf - 1)
6433 ig = s_Molecule_AtomGroupFromValue(self, range);
6434 if (ig == NULL || IntGroupGetCount(ig) == 0)
6438 /* Use the residue number of the first specified atom */
6439 n = IntGroupGetNthPoint(ig, 0);
6440 if (n >= mol->natoms)
6441 rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
6442 ap = ATOM_AT_INDEX(mol->atoms, n);
6445 /* Change the residue number */
6446 MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
6447 /* Change the residue name if necessary */
6451 seqs[1] = kInvalidIndex; */
6452 MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
6454 IntGroupRelease(ig);
6460 * offset_residue(group, offset) -> Molecule
6462 * Offset the residue number of the specified atoms. If any of the residue number gets
6463 * negative, then exception is thrown.
6464 * This operation is undoable.
6467 s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
6472 Data_Get_Struct(self, Molecule, mol);
6473 ig = s_Molecule_AtomGroupFromValue(self, range);
6474 ofs = NUM2INT(offset);
6475 result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
6477 rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
6478 IntGroupRelease(ig);
6484 * renumber_atoms(array) -> IntGroup
6486 * Change the order of atoms so that the atoms specified in the array argument appear
6487 * in this order from the top of the molecule. The atoms that are not included in array
6488 * are placed after these atoms, and these atoms are returned as an intGroup.
6489 * This operation is undoable.
6492 s_Molecule_RenumberAtoms(VALUE self, VALUE array)
6498 VALUE *valp, retval;
6499 Data_Get_Struct(self, Molecule, mol);
6500 if (TYPE(array) != T_ARRAY)
6501 array = rb_funcall(array, rb_intern("to_a"), 0);
6502 n = RARRAY_LEN(array);
6503 valp = RARRAY_PTR(array);
6504 new2old = ALLOC_N(Int, n + 1);
6505 for (i = 0; i < n; i++)
6506 new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
6507 new2old[i] = kInvalidIndex;
6508 i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
6510 rb_raise(rb_eMolbyError, "Atom index out of range");
6512 rb_raise(rb_eMolbyError, "Duplicate entry");
6514 rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
6515 retval = IntGroup_Alloc(rb_cIntGroup);
6516 Data_Get_Struct(retval, IntGroup, ig);
6517 if (mol->natoms > n)
6518 IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
6525 * find_close_atoms(atom, limit = 1.2) -> array of Integers (atom indices)
6527 * Find atoms that are within the threshold distance from the given atom.
6528 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6529 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6530 * If limit is not given, a default value of 1.2 is used.
6531 * An array of atom indices is returned. If no atoms are found, an empty array is returned.
6534 s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
6539 Int n1, nbonds, *bonds;
6540 Data_Get_Struct(self, Molecule, mol);
6541 rb_scan_args(argc, argv, "11", &aval, &limval);
6542 n1 = s_Molecule_AtomIndexFromValue(mol, aval);
6546 limit = NUM2DBL(rb_Float(limval));
6547 nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
6549 MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
6550 aval = rb_ary_new();
6552 for (n1 = 0; n1 < nbonds; n1++)
6553 rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
6561 * guess_bonds(limit = 1.2) -> Integer
6563 * Create bonds between atoms that are within the threshold distance.
6564 * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
6565 * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
6566 * If limit is not given, a default value of 1.2 is used.
6567 * The number of the newly created bonds is returned.
6568 * This operation is undoable.
6571 s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
6577 Data_Get_Struct(self, Molecule, mol);
6578 rb_scan_args(argc, argv, "01", &limval);
6582 limit = NUM2DBL(rb_Float(limval));
6583 MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
6585 MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds);
6588 return INT2NUM(nbonds);
6593 * register_undo(script, *args)
6595 * Register an undo operation with the current molecule.
6598 s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
6603 Data_Get_Struct(self, Molecule, mol);
6604 rb_scan_args(argc, argv, "1*", &script, &args);
6605 act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
6606 MolActionCallback_registerUndo(mol, act);
6612 * undo_enabled? -> bool
6614 * Returns true if undo is enabled for this molecule; otherwise no.
6617 s_Molecule_UndoEnabled(VALUE self)
6620 Data_Get_Struct(self, Molecule, mol);
6621 if (MolActionCallback_isUndoRegistrationEnabled(mol))
6628 * undo_enabled = bool
6630 * Enable or disable undo.
6633 s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
6636 Data_Get_Struct(self, Molecule, mol);
6637 MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
6643 * selection -> IntGroup
6645 * Returns the current selection.
6648 s_Molecule_Selection(VALUE self)
6653 Data_Get_Struct(self, Molecule, mol);
6654 if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
6655 ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
6656 val = ValueFromIntGroup(ig);
6657 IntGroupRelease(ig);
6659 val = IntGroup_Alloc(rb_cIntGroup);
6665 s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
6669 Data_Get_Struct(self, Molecule, mol);
6673 ig = s_Molecule_AtomGroupFromValue(self, val);
6675 MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
6677 MoleculeSetSelection(mol, ig);
6679 IntGroupRelease(ig);
6685 * selection = IntGroup
6687 * Set the current selection. The right-hand operand may be nil.
6688 * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
6691 s_Molecule_SetSelection(VALUE self, VALUE val)
6693 return s_Molecule_SetSelectionSub(self, val, 0);
6698 * set_undoable_selection(IntGroup)
6700 * Set the current selection with undo registration. The right-hand operand may be nil.
6701 * This operation is undoable.
6704 s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
6706 return s_Molecule_SetSelectionSub(self, val, 1);
6711 * hidden_atoms -> IntGroup
6713 * Returns the currently hidden atoms.
6716 s_Molecule_HiddenAtoms(VALUE self)
6718 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
6719 return Qnil; /* Not reached */
6723 Data_Get_Struct(self, Molecule, mol);
6728 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6729 if (ap->exflags & kAtomHiddenFlag)
6730 IntGroupAdd(ig, i, 1);
6732 val = ValueFromIntGroup(ig);
6733 IntGroupRelease(ig);
6736 } else return Qnil; */
6741 * set_hidden_atoms(IntGroup)
6742 * self.hidden_atoms = IntGroup
6744 * Hide the specified atoms. This operation is _not_ undoable.
6747 s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
6749 rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
6750 return Qnil; /* Not reached */
6753 Data_Get_Struct(self, Molecule, mol);
6761 ig = s_Molecule_AtomGroupFromValue(self, val);
6762 for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
6763 if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
6764 ap->exflags |= kAtomHiddenFlag;
6766 ap->exflags &= kAtomHiddenFlag;
6770 IntGroupRelease(ig);
6771 MoleculeCallback_notifyModification(mol, 0);
6778 * select_frame(index)
6781 * Select the specified frame. If successful, returns true, otherwise returns false.
6784 s_Molecule_SelectFrame(VALUE self, VALUE val)
6787 int ival = NUM2INT(val);
6788 Data_Get_Struct(self, Molecule, mol);
6789 ival = MoleculeSelectFrame(mol, ival, 1);
6799 * Get the current frame.
6802 s_Molecule_Frame(VALUE self)
6805 Data_Get_Struct(self, Molecule, mol);
6806 return INT2NUM(mol->cframe);
6811 * nframes -> Integer
6813 * Get the number of frames.
6816 s_Molecule_Nframes(VALUE self)
6819 Data_Get_Struct(self, Molecule, mol);
6820 return INT2NUM(MoleculeGetNumberOfFrames(mol));
6825 * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
6826 * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
6828 * Insert new frames at the indices specified by the intGroup. If the first argument is
6829 * an integer, a single new frame is inserted at that index. If the first argument is
6830 * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
6831 * should be an array of arrays of Vector3Ds, then those coordinates are set
6832 * to the new frame. Otherwise, the coordinates of current molecule are copied
6834 * Returns an intGroup representing the inserted frames if successful, nil if not.
6837 s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
6839 VALUE val, coords, cells;
6842 int count, ival, i, j, len, len_c, len2, nframes;
6845 Data_Get_Struct(self, Molecule, mol);
6846 rb_scan_args(argc, argv, "12", &val, &coords, &cells);
6847 if (coords != Qnil) {
6848 if (TYPE(coords) != T_ARRAY)
6849 rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
6850 len = RARRAY_LEN(coords);
6852 if (cells != Qnil) {
6853 if (mol->cell == NULL)
6854 rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
6855 if (TYPE(cells) != T_ARRAY)
6856 rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
6857 len_c = RARRAY_LEN(cells);
6859 count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
6860 nframes = MoleculeGetNumberOfFrames(mol);
6862 ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
6863 val = ValueFromIntGroup(ig);
6865 ig = IntGroupFromValue(val);
6867 count = IntGroupGetCount(ig); /* Count is updated here */
6868 vp = ALLOC_N(Vector, mol->natoms * count);
6870 vp2 = ALLOC_N(Vector, 4 * count);
6874 rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
6875 ptr = RARRAY_PTR(coords);
6876 for (i = 0; i < count; i++) {
6877 if (TYPE(ptr[i]) != T_ARRAY)
6878 rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
6879 len2 = RARRAY_LEN(ptr[i]);
6880 if (len2 < mol->natoms)
6881 rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
6882 ptr2 = RARRAY_PTR(ptr[i]);
6883 for (j = 0; j < mol->natoms; j++)
6884 VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
6888 for (i = 0; i < count; i++) {
6889 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
6890 vp[i * mol->natoms + j] = ap->r;
6896 rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
6897 ptr = RARRAY_PTR(cells);
6898 for (i = 0; i < count; i++) {
6899 if (TYPE(ptr[i]) != T_ARRAY)
6900 rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
6901 len2 = RARRAY_LEN(ptr[i]);
6903 rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
6904 ptr2 = RARRAY_PTR(ptr[i]);
6905 for (j = 0; j < 4; j++)
6906 VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
6909 ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
6910 IntGroupRelease(ig);
6914 return (ival >= 0 ? val : Qnil);
6919 * create_frame(coordinates = nil) -> Integer
6920 * create_frames(coordinates = nil) -> Integer
6922 * Same as molecule.insert_frames(nil, coordinates).
6925 s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
6928 rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
6930 return s_Molecule_InsertFrames(3, vals, self);
6935 * remove_frames(IntGroup, wantCoordinates = false)
6937 * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
6938 * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
6939 * removed frames is returned if operation is successful.
6942 s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
6949 Data_Get_Struct(self, Molecule, mol);
6950 rb_scan_args(argc, argv, "11", &val, &flag);
6951 ig = IntGroupFromValue(val);
6952 count = IntGroupGetCount(ig);
6954 /* Create return value before removing frames */
6959 retval = rb_ary_new2(count);
6960 for (i = 0; i < count; i++) {
6961 n = IntGroupGetNthPoint(ig, i);
6962 coords = rb_ary_new2(mol->natoms);
6963 for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
6964 if (n < ap->nframes && n != mol->cframe)
6967 rb_ary_push(coords, ValueFromVector(&v));
6969 rb_ary_push(retval, coords);
6971 } else retval = Qtrue;
6972 if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
6979 * each_frame {|n| ...}
6981 * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
6982 * the frame number. After completion, the original frame number is restored.
6985 s_Molecule_EachFrame(VALUE self)
6987 int i, cframe, nframes;
6989 Data_Get_Struct(self, Molecule, mol);
6990 cframe = mol->cframe;
6991 nframes = MoleculeGetNumberOfFrames(mol);
6993 for (i = 0; i < nframes; i++) {
6994 MoleculeSelectFrame(mol, i, 1);
6995 rb_yield(INT2NUM(i));
6997 MoleculeSelectFrame(mol, cframe, 1);
7004 * set_atom_attr(index, key, value)
7006 * Set the atom attribute for the specified atom.
7007 * This operation is undoable.
7010 s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
7014 Data_Get_Struct(self, Molecule, mol);
7015 aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
7016 oldval = s_AtomRef_GetAttr(aref, key);
7019 s_AtomRef_SetAttr(aref, key, val);
7025 * get_atom_attr(index, key)
7027 * Get the atom attribute for the specified atom.
7030 s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
7032 return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
7037 * get_coord_from_frame(index, group = nil, cflag = nil)
7039 * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
7040 * are modified. If cflag is true, the cell parameter is also copied (if present).
7043 s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
7046 VALUE ival, gval, cval;
7047 Int index, i, j, n, nn;
7049 IntGroupIterator iter;
7052 Data_Get_Struct(self, Molecule, mol);
7053 rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
7054 index = NUM2INT(rb_Integer(ival));
7055 if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
7057 rb_raise(rb_eMolbyError, "No frame is present");
7059 rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
7062 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
7064 ig = s_Molecule_AtomGroupFromValue(self, gval);
7066 n = IntGroupGetCount(ig);
7068 vp = (Vector *)calloc(sizeof(Vector), n);
7069 IntGroupIteratorInit(ig, &iter);
7072 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7073 ap = ATOM_AT_INDEX(mol->atoms, i);
7074 if (index < ap->nframes) {
7075 vp[j] = ap->frames[index];
7083 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
7085 if (RTEST(cval) && mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
7086 vp = mol->frame_cells + index * 4;
7087 MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
7089 IntGroupIteratorRelease(&iter);
7091 IntGroupRelease(ig);
7097 * fragment(n1, *exatoms) -> IntGroup
7098 * fragment(group, *exatoms) -> IntGroup
7100 * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
7101 * those atoms will not be counted during the search.
7104 s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
7107 IntGroup *baseg, *ig, *exatoms;
7109 volatile VALUE nval, exval;
7110 Data_Get_Struct(self, Molecule, mol);
7111 rb_scan_args(argc, argv, "1*", &nval, &exval);
7112 if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
7114 n = NUM2INT(s_Molecule_AtomIndex(self, nval));
7116 baseg = s_Molecule_AtomGroupFromValue(self, nval);
7118 if (RARRAY_LEN(exval) == 0) {
7121 exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
7122 Data_Get_Struct(exval, IntGroup, exatoms);
7124 if (baseg == NULL) {
7125 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7127 IntGroupIterator iter;
7128 IntGroupIteratorInit(baseg, &iter);
7129 if ((n = IntGroupIteratorNext(&iter)) < 0) {
7132 ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7134 while ((n = IntGroupIteratorNext(&iter)) >= 0) {
7136 subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
7138 IntGroupAddIntGroup(ig, subg);
7139 IntGroupRelease(subg);
7144 IntGroupIteratorRelease(&iter);
7145 IntGroupRelease(baseg);
7148 rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
7149 nval = ValueFromIntGroup(ig);
7150 IntGroupRelease(ig);
7156 * each_fragment {|group| ...}
7158 * Execute the block, with the IntGroup object for each fragment as the argument.
7159 * Atoms or bonds should not be added or removed during the execution of the block.
7162 s_Molecule_EachFragment(VALUE self)
7167 Data_Get_Struct(self, Molecule, mol);
7168 if (mol == NULL || mol->natoms == 0)
7170 ag = IntGroupNewWithPoints(0, mol->natoms, -1);
7171 while (IntGroupGetCount(ag) > 0) {
7172 int n = IntGroupGetNthPoint(ag, 0);
7173 fg = MoleculeFragmentExcludingAtomGroup(mol, n, NULL);
7175 rb_raise(rb_eMolbyError, "internal error during each_fragment");
7176 gval = ValueFromIntGroup(fg);
7178 IntGroupRemoveIntGroup(ag, fg);
7179 IntGroupRelease(fg);
7181 IntGroupRelease(ag);
7187 * detachable?(group) -> [n1, n2]
7189 * Check whether the group is 'detachable', i.e. the group is bound to the rest
7190 * of the molecule via only one bond. If it is, then the indices of the atoms
7191 * belonging to the bond is returned, the first element being the atom included
7192 * in the fragment. Otherwise, Qnil is returned.
7195 s_Molecule_Detachable_P(VALUE self, VALUE gval)
7201 Data_Get_Struct(self, Molecule, mol);
7202 ig = s_Molecule_AtomGroupFromValue(self, gval);
7203 if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
7204 retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
7205 } else retval = Qnil;
7206 IntGroupRelease(ig);
7212 * bonds_on_border(group = selection) -> Array of Array of two Integers
7214 * Returns an array of bonds that connect an atom in the group and an atom out
7215 * of the group. The first atom in the bond always belongs to the group. If no
7216 * such bonds are present, an empty array is returned.
7219 s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
7224 Data_Get_Struct(self, Molecule, mol);
7225 rb_scan_args(argc, argv, "01", &gval);
7227 ig = MoleculeGetSelection(mol);
7231 ig = s_Molecule_AtomGroupFromValue(self, gval);
7233 retval = rb_ary_new();
7236 bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
7238 IntGroupIterator iter;
7240 IntGroupIteratorInit(bg, &iter);
7241 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
7242 /* The atoms at the border */
7244 n1 = mol->bonds[i * 2];
7245 n2 = mol->bonds[i * 2 + 1];
7246 if (IntGroupLookupPoint(ig, n1) < 0) {
7250 if (IntGroupLookupPoint(ig, n1) < 0)
7251 continue; /* Actually this is an internal error */
7253 rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
7255 IntGroupIteratorRelease(&iter);
7257 IntGroupRelease(bg);
7258 IntGroupRelease(ig);
7264 * translate(vec, group = nil) -> Molecule
7266 * Translate the molecule by vec. If group is given, only atoms in the group are moved.
7267 * This operation is undoable.
7270 s_Molecule_Translate(int argc, VALUE *argv, VALUE self)
7276 Data_Get_Struct(self, Molecule, mol);
7277 rb_scan_args(argc, argv, "11", &vec, &group);
7278 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7279 VectorFromValue(vec, &v);
7280 // MoleculeTranslate(mol, &v, ig);
7281 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
7283 IntGroupRelease(ig);
7289 * rotate(axis, angle, center = [0,0,0], group = nil) -> Molecule
7291 * Rotate the molecule. The axis must not a zero vector. angle is given in degree.
7292 * If group is given, only atoms in the group are moved.
7293 * This operation is undoable.
7296 s_Molecule_Rotate(int argc, VALUE *argv, VALUE self)
7299 volatile VALUE aval, anval, cval, gval;
7304 Data_Get_Struct(self, Molecule, mol);
7305 rb_scan_args(argc, argv, "22", &aval, &anval, &cval, &gval);
7306 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7307 angle = NUM2DBL(rb_Float(anval)) * kDeg2Rad;
7308 VectorFromValue(aval, &av);
7310 cv.x = cv.y = cv.z = 0.0;
7312 VectorFromValue(cval, &cv);
7313 if (TransformForRotation(tr, &av, angle, &cv))
7314 rb_raise(rb_eMolbyError, "rotation axis cannot be a zero vector");
7315 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7317 IntGroupRelease(ig);
7323 * reflect(axis, center = [0,0,0], group = nil) -> Molecule
7325 * Reflect the molecule by the plane which is perpendicular to axis and including center.
7326 * axis must not be a zero vector.
7327 * If group is given, only atoms in the group are moved.
7328 * This operation is undoable.
7331 s_Molecule_Reflect(int argc, VALUE *argv, VALUE self)
7334 volatile VALUE aval, cval, gval;
7338 Data_Get_Struct(self, Molecule, mol);
7339 rb_scan_args(argc, argv, "12", &aval, &cval, &gval);
7340 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7341 VectorFromValue(aval, &av);
7343 cv.x = cv.y = cv.z = 0.0;
7345 VectorFromValue(cval, &cv);
7346 if (TransformForReflection(tr, &av, &cv))
7347 rb_raise(rb_eMolbyError, "reflection axis cannot be a zero vector");
7348 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7350 IntGroupRelease(ig);
7356 * invert(center = [0,0,0], group = nil) -> Molecule
7358 * Invert the molecule with the given center.
7359 * If group is given, only atoms in the group are moved.
7360 * This operation is undoable.
7363 s_Molecule_Invert(int argc, VALUE *argv, VALUE self)
7366 volatile VALUE cval, gval;
7370 Data_Get_Struct(self, Molecule, mol);
7371 rb_scan_args(argc, argv, "02", &cval, &gval);
7372 ig = (NIL_P(gval) ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
7374 cv.x = cv.y = cv.z = 0.0;
7376 VectorFromValue(cval, &cv);
7377 TransformForInversion(tr, &cv);
7378 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7380 IntGroupRelease(ig);
7386 * transform(transform, group = nil) -> Molecule
7388 * Transform the molecule by the given Transform object.
7389 * If group is given, only atoms in the group are moved.
7390 * This operation is undoable.
7393 s_Molecule_Transform(int argc, VALUE *argv, VALUE self)
7399 Data_Get_Struct(self, Molecule, mol);
7400 rb_scan_args(argc, argv, "11", &trans, &group);
7401 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7402 TransformFromValue(trans, &tr);
7403 /* MoleculeTransform(mol, tr, ig); */
7404 MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
7406 IntGroupRelease(ig);
7411 s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
7413 switch (MoleculeCenterOfMass(mol, outv, ig)) {
7414 case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
7415 case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
7417 default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
7423 * center_of_mass(group = nil) -> Vector3D
7425 * Calculate the center of mass for the given set of atoms. The argument
7426 * group is null, then all atoms are considered.
7429 s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
7435 Data_Get_Struct(self, Molecule, mol);
7436 rb_scan_args(argc, argv, "01", &group);
7437 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7438 s_Molecule_DoCenterOfMass(mol, &v, ig);
7440 IntGroupRelease(ig);
7441 return ValueFromVector(&v);
7446 * centralize(group = nil) -> self
7448 * Translate the molecule so that the center of mass of the given group is located
7449 * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
7452 s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
7458 Data_Get_Struct(self, Molecule, mol);
7459 rb_scan_args(argc, argv, "01", &group);
7460 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7461 s_Molecule_DoCenterOfMass(mol, &v, ig);
7463 IntGroupRelease(ig);
7467 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
7473 * bounds(group = nil) -> [min, max]
7475 * Calculate the boundary. The return value is an array of two Vector3D objects.
7478 s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
7486 Data_Get_Struct(self, Molecule, mol);
7487 rb_scan_args(argc, argv, "01", &group);
7488 ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
7489 if (ig != NULL && IntGroupGetCount(ig) == 0)
7490 rb_raise(rb_eMolbyError, "atom group is empty");
7491 vmin.x = vmin.y = vmin.z = 1e30;
7492 vmax.x = vmax.y = vmax.z = -1e30;
7493 for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
7495 if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
7511 return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
7514 /* Get atom position or a vector */
7516 s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
7518 if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
7519 int n1 = s_Molecule_AtomIndexFromValue(mol, val);
7520 *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
7522 VectorFromValue(val, vp);
7528 * measure_bond(n1, n2) -> Float
7530 * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
7531 * or Vector3D values.
7532 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7535 s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
7539 Data_Get_Struct(self, Molecule, mol);
7540 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7541 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7542 return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
7547 * measure_angle(n1, n2, n3) -> Float
7549 * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
7550 * or Vector3D values. The return value is in degree.
7551 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7554 s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
7559 Data_Get_Struct(self, Molecule, mol);
7560 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7561 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7562 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7563 d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
7565 return Qnil; /* Cannot define */
7566 else return rb_float_new(d);
7571 * measure_dihedral(n1, n2, n3, n4) -> Float
7573 * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
7574 * or Vector3D values. The return value is in degree.
7575 * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
7578 s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
7581 Vector v1, v2, v3, v4;
7583 Data_Get_Struct(self, Molecule, mol);
7584 s_Molecule_GetVectorFromArg(mol, nval1, &v1);
7585 s_Molecule_GetVectorFromArg(mol, nval2, &v2);
7586 s_Molecule_GetVectorFromArg(mol, nval3, &v3);
7587 s_Molecule_GetVectorFromArg(mol, nval4, &v4);
7588 d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
7590 return Qnil; /* Cannot define */
7591 else return rb_float_new(d);
7596 * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
7598 * Expand the specified part of the molecule by the given symmetry operation.
7599 * Returns the array of atom indices corresponding to the expanded atoms.
7602 s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
7605 VALUE gval, sval, xval, yval, zval, rval;
7611 Data_Get_Struct(self, Molecule, mol);
7612 rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
7613 n[0] = NUM2INT(rb_Integer(sval));
7614 n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
7615 n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
7616 n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
7617 ig = s_Molecule_AtomGroupFromValue(self, gval);
7618 if (n[0] < 0 || n[0] >= mol->nsyms)
7619 rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
7620 natoms = mol->natoms;
7622 MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
7624 rval = rb_ary_new2(nidx);
7625 while (--nidx >= 0) {
7626 rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
7628 /* if (natoms == mol->natoms)
7631 rval = IntGroup_Alloc(rb_cIntGroup);
7632 Data_Get_Struct(rval, IntGroup, ig);
7633 IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
7640 * amend_by_symmetry(group = nil) -> IntGroup
7642 * Expand the specified part of the molecule by the given symmetry operation.
7643 * Returns an IntGroup containing the added atoms.
7646 s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
7651 Data_Get_Struct(self, Molecule, mol);
7652 rb_scan_args(argc, argv, "01", &gval);
7654 ig = s_Molecule_AtomGroupFromValue(self, gval);
7656 MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
7657 rval = ValueFromIntGroup(ig2);
7658 IntGroupRelease(ig2);
7664 * transform_for_symop(symop, is_cartesian = nil) -> Transform
7666 * Get the transform corresponding to the symmetry operation. The symop can either be
7667 * an integer (index of symmetry operation) or [sym, dx, dy, dz].
7668 * If is_cartesian is true, the returned transform is for cartesian coordinates.
7669 * Otherwise, the returned transform is for fractional coordinates.
7670 * Raises exception when no cell or no transform are defined.
7673 s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
7679 Data_Get_Struct(self, Molecule, mol);
7680 if (mol->cell == NULL)
7681 rb_raise(rb_eMolbyError, "no unit cell is defined");
7682 if (mol->nsyms == 0)
7683 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7684 rb_scan_args(argc, argv, "11", &sval, &fval);
7685 if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
7686 symop.sym = NUM2INT(rb_Integer(sval));
7687 symop.dx = symop.dy = symop.dz = 0;
7689 sval = rb_ary_to_ary(sval);
7690 if (RARRAY_LEN(sval) < 4)
7691 rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
7692 symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
7693 symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
7694 symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
7695 symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
7697 if (symop.sym >= mol->nsyms)
7698 rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
7699 MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
7700 return ValueFromTransform(&tr);
7705 * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
7707 * Get the symmetry operation corresponding to the given transform.
7708 * If is_cartesian is true, the given transform is for cartesian coordinates.
7709 * Otherwise, the given transform is for fractional coordinates.
7710 * Raises exception when no cell or no transform are defined.
7713 s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
7720 Data_Get_Struct(self, Molecule, mol);
7721 if (mol->cell == NULL)
7722 rb_raise(rb_eMolbyError, "no unit cell is defined");
7723 if (mol->nsyms == 0)
7724 rb_raise(rb_eMolbyError, "no symmetry operation is defined");
7725 rb_scan_args(argc, argv, "11", &tval, &fval);
7726 TransformFromValue(tval, &tr);
7727 n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
7729 return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
7731 return Qnil; /* Not found */
7737 * wrap_unit_cell(group) -> Vector3D
7739 * Move the specified group so that the center of mass of the group is within the
7740 * unit cell. The offset vector is returned. If no periodic box is defined,
7741 * exception is raised.
7744 s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
7749 Data_Get_Struct(self, Molecule, mol);
7750 if (mol->cell == NULL)
7751 rb_raise(rb_eMolbyError, "no unit cell is defined");
7752 ig = s_Molecule_AtomGroupFromValue(self, gval);
7753 s_Molecule_DoCenterOfMass(mol, &cv, ig);
7754 TransformVec(&v, mol->cell->rtr, &cv);
7755 if (mol->cell->flags[0])
7757 if (mol->cell->flags[1])
7759 if (mol->cell->flags[2])
7761 TransformVec(&dv, mol->cell->tr, &v);
7763 MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
7764 IntGroupRelease(ig);
7765 return ValueFromVector(&dv);
7770 * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
7772 * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
7773 * first and second atom in the pair should belong to group1 and group2, respectively.
7774 * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
7777 s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
7780 VALUE limval, gval1, gval2, rval, igval;
7781 IntGroup *ig1, *ig2;
7782 IntGroupIterator iter1, iter2;
7788 MDExclusion *exinfo;
7791 Data_Get_Struct(self, Molecule, mol);
7792 rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
7793 lim = NUM2DBL(rb_Float(limval));
7795 rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
7797 ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
7799 ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
7801 ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
7803 ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
7805 if (!RTEST(igval)) {
7806 /* Use the exclusion table in MDArena */
7807 if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
7808 VALUE mval = ValueFromMolecule(mol);
7809 s_RebuildMDParameterIfNecessary(mval, Qnil);
7811 exinfo = mol->arena->exinfo; /* May be NULL */
7812 exlist = mol->arena->exlist;
7817 IntGroupIteratorInit(ig1, &iter1);
7818 IntGroupIteratorInit(ig2, &iter2);
7821 while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
7823 ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
7825 if (exinfo != NULL) {
7826 exn1 = exinfo[n[0]].index1;
7827 exn2 = exinfo[n[0] + 1].index1;
7828 } else exn1 = exn2 = -1;
7829 IntGroupIteratorReset(&iter2);
7830 while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
7831 ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
7833 continue; /* Same atom */
7834 if (exinfo != NULL) {
7835 /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
7836 for (i = exn1; i < exn2; i++) {
7837 if (exlist[i] == n[1])
7841 continue; /* Should be excluded */
7843 if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
7844 /* Is this pair already registered? */
7846 for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
7847 if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
7851 /* Not registered yet */
7852 AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
7857 IntGroupIteratorRelease(&iter2);
7858 IntGroupIteratorRelease(&iter1);
7859 IntGroupRelease(ig2);
7860 IntGroupRelease(ig1);
7861 rval = rb_ary_new2(npairs);
7862 if (pairs != NULL) {
7863 for (i = 0; i < npairs; i++) {
7864 rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
7871 /* Calculate the transform that moves the current coordinates to the reference
7872 coordinates with least displacements. */
7874 s_Molecule_FitCoordinates_Sub(Molecule *mol, IntGroup *ig, Vector *ref, Double *weights, Transform trans)
7882 Double eigen_val[3];
7883 Vector eigen_vec[3];
7885 IntGroupIterator iter;
7887 natoms = mol->natoms;
7889 IntGroupIteratorInit(ig, &iter);
7891 /* Calculate the weighted center */
7895 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7896 ap1 = ATOM_AT_INDEX(ap, in);
7897 w1 = (weights != NULL ? weights[i] : ap1->weight);
7898 VecScaleInc(org1, ap1->r, w1);
7899 VecScaleInc(org2, ref[i], w1);
7903 VecScaleSelf(org1, w);
7904 VecScaleSelf(org2, w);
7906 /* R = sum(weight[n]^2 * x[n] * t(y[n])); */
7907 /* Matrix to diagonalize = R * tR */
7908 memset(r, 0, sizeof(Mat33));
7909 memset(q, 0, sizeof(Mat33));
7910 memset(u, 0, sizeof(Mat33));
7912 IntGroupIteratorReset(&iter);
7913 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7915 ap1 = ATOM_AT_INDEX(ap, in);
7916 w1 = (weights != NULL ? weights[i] : ap1->weight);
7918 VecSub(v1, ap1->r, org1);
7919 VecSub(v2, ref[i], org2);
7920 r[0] += w1 * v1.x * v2.x;
7921 r[1] += w1 * v1.y * v2.x;
7922 r[2] += w1 * v1.z * v2.x;
7923 r[3] += w1 * v1.x * v2.y;
7924 r[4] += w1 * v1.y * v2.y;
7925 r[5] += w1 * v1.z * v2.y;
7926 r[6] += w1 * v1.x * v2.z;
7927 r[7] += w1 * v1.y * v2.z;
7928 r[8] += w1 * v1.z * v2.z;
7931 for (i = 0; i < 9; i++)
7933 for (i = 0; i < 3; i++) {
7934 for (j = 0; j < 3; j++) {
7935 for (k = 0; k < 3; k++) {
7936 q[i+j*3] += r[i+k*3] * r[j+k*3];
7941 if (MatrixSymDiagonalize(q, eigen_val, eigen_vec) != 0) {
7942 IntGroupIteratorRelease(&iter);
7943 return -1.0; /* Cannot determine the eigenvector */
7946 /* s[i] = tR * v[i] / sqrt(eigenval[i]) */
7947 /* U = s0*t(v0) + s1*t(v1) + s2*t(v2) */
7948 MatrixTranspose(r, r);
7949 for (i = 0; i < 3; i++) {
7950 MatrixVec(&s[i], r, &eigen_vec[i]);
7951 w1 = 1.0 / sqrt(eigen_val[i]);
7952 VecScaleSelf(s[i], w1);
7954 for (k = 0; k < 3; k++) {
7955 u[0] += s[k].x * eigen_vec[k].x;
7956 u[1] += s[k].y * eigen_vec[k].x;
7957 u[2] += s[k].z * eigen_vec[k].x;
7958 u[3] += s[k].x * eigen_vec[k].y;
7959 u[4] += s[k].y * eigen_vec[k].y;
7960 u[5] += s[k].z * eigen_vec[k].y;
7961 u[6] += s[k].x * eigen_vec[k].z;
7962 u[7] += s[k].y * eigen_vec[k].z;
7963 u[8] += s[k].z * eigen_vec[k].z;
7966 /* y = U*(x - org1) + org2 = U*x + (org2 - U*org1) */
7967 MatrixVec(&org1, u, &org1);
7969 for (i = 0; i < 9; i++)
7975 /* Calculate rmsd */
7976 IntGroupIteratorReset(&iter);
7978 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
7980 ap1 = ATOM_AT_INDEX(ap, in);
7981 TransformVec(&tv, trans, &ap1->r);
7983 w += VecLength2(tv);
7986 IntGroupIteratorRelease(&iter);
7992 * fit_coordinates(group, ref, weight = nil) -> [transform, rmsd]
7994 * Calculate the transform to fit the given group to the set of reference coordinates.
7995 * The reference coordinates ref is given as either a frame number, an array of
7996 * Vector3Ds or arrays, or an LAMatrix. Weight can be optionally given as an array
7997 * of numbers or an LAMatrix. If weight is not given, the atomic weights are used.
7998 * Return values are the transform (that converts the present coordinates to the
7999 * target coordinates) and root mean square deviation (without weight).
8002 s_Molecule_FitCoordinates(int argc, VALUE *argv, VALUE self)
8006 VALUE gval, rval, wval;
8008 IntGroupIterator iter;
8009 int nn, errno, i, j, in, status;
8011 Double *weights, dval[3];
8014 Data_Get_Struct(self, Molecule, mol);
8015 rb_scan_args(argc, argv, "21", &gval, &rval, &wval);
8017 ig = IntGroupNewWithPoints(0, mol->natoms, -1);
8019 ig = IntGroupFromValue(gval);
8020 if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
8021 IntGroupRelease(ig);
8022 rb_raise(rb_eMolbyError, "atom group is not given correctly");
8024 ref = (Vector *)calloc(sizeof(Vector), nn);
8025 weights = (Double *)calloc(sizeof(Double), nn);
8026 IntGroupIteratorInit(ig, &iter);
8027 if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
8028 int fn = NUM2INT(rb_Integer(rval));
8029 if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
8034 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8035 ap = ATOM_AT_INDEX(mol->atoms, in);
8036 if (fn < ap->nframes)
8037 ref[i] = ap->frames[fn];
8038 else ref[i] = ap->r;
8040 } else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
8041 LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
8042 if (m->row * m->column < nn * 3) {
8046 for (i = 0; i < nn; i++) {
8047 ref[i].x = m->data[i * 3];
8048 ref[i].y = m->data[i * 3 + 1];
8049 ref[i].z = m->data[i * 3 + 2];
8053 rval = rb_protect(rb_ary_to_ary, rval, &status);
8058 if (RARRAY_LEN(rval) < nn) {
8062 if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
8063 /* Array of 3*nn numbers */
8064 if (RARRAY_LEN(rval) < nn * 3) {
8068 for (i = 0; i < nn; i++) {
8069 for (j = 0; j < 3; j++) {
8070 aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
8075 dval[j] = NUM2DBL(aval);
8082 /* Array of nn Vector3Ds or Arrays */
8083 for (i = 0; i < nn; i++) {
8084 aval = (RARRAY_PTR(rval))[i];
8085 if (rb_obj_is_kind_of(aval, rb_cVector3D)) {
8086 VectorFromValue(aval, &ref[i]);
8088 aval = rb_protect(rb_ary_to_ary, aval, &status);
8093 if (RARRAY_LEN(aval) < 3) {
8098 for (j = 0; j < 3; j++) {
8099 VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
8104 dval[j] = NUM2DBL(aaval);
8114 /* Use atomic weights */
8115 IntGroupIteratorReset(&iter);
8116 for (i = 0; (in = IntGroupIteratorNext(&iter)) >= 0; i++) {
8117 ap = ATOM_AT_INDEX(mol->atoms, in);
8118 weights[i] = ap->weight;
8121 wval = rb_protect(rb_ary_to_ary, wval, &status);
8126 if (RARRAY_LEN(wval) < nn) {
8130 for (i = 0; i < nn; i++) {
8131 VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
8136 weights[i] = NUM2DBL(wwval);
8139 dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
8146 IntGroupIteratorRelease(&iter);
8150 return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
8151 } else if (errno == 1) {
8152 rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
8153 } else if (errno == 2) {
8154 rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
8155 } else if (errno == 3) {
8156 rb_jump_tag(status);
8157 } else if (errno == 4) {
8158 rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
8159 } else if (errno == 5) {
8160 rb_raise(rb_eMolbyError, "insufficient number of weight values");
8161 } else if (errno == 6) {
8162 rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
8164 return Qnil; /* Not reached */
8171 * Refresh the display if this molecule is bound to a view. Otherwise do nothing.
8174 s_Molecule_Display(VALUE self)
8177 Data_Get_Struct(self, Molecule, mol);
8178 if (mol->mview != NULL)
8179 MainViewCallback_display(mol->mview);
8187 * Make the window frontmost if this molecule is bound to a view. Otherwise do nothing.
8190 s_Molecule_MakeFront(VALUE self)
8193 Data_Get_Struct(self, Molecule, mol);
8194 if (mol->mview != NULL)
8195 MainViewCallback_makeFront(mol->mview);
8201 * update_enabled? -> bool
8203 * Returns true if screen update is enabled; otherwise no.
8206 s_Molecule_UpdateEnabled(VALUE self)
8209 Data_Get_Struct(self, Molecule, mol);
8210 if (mol->mview != NULL && !mol->mview->freezeScreen)
8217 * update_enabled = bool
8219 * Enable or disable screen update. This is effective for automatic update on modification.
8220 * Explicit call to molecule.display() always updates the screen.
8223 s_Molecule_SetUpdateEnabled(VALUE self, VALUE val)
8226 Data_Get_Struct(self, Molecule, mol);
8227 val = ((val != Qfalse && val != Qnil) ? Qtrue : Qfalse);
8228 if (mol->mview != NULL)
8229 mol->mview->freezeScreen = (val == Qfalse);
8237 * show_unitcell(bool)
8238 * show_unitcell = bool
8240 * Set the flag whether to show the unit cell. If no argument is given, the
8241 * current flag is returned.
8244 s_Molecule_ShowUnitCell(int argc, VALUE *argv, VALUE self)
8247 Data_Get_Struct(self, Molecule, mol);
8248 if (mol->mview == NULL)
8251 mol->mview->showUnitCell = (RTEST(argv[0]) != 0);
8252 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8254 return (mol->mview->showUnitCell ? Qtrue : Qfalse);
8260 * show_hydrogens(bool)
8261 * show_hydrogens = bool
8263 * Set the flag whether to show the hydrogen atoms. If no argument is given, the
8264 * current flag is returned.
8267 s_Molecule_ShowHydrogens(int argc, VALUE *argv, VALUE self)
8270 Data_Get_Struct(self, Molecule, mol);
8271 if (mol->mview == NULL)
8274 mol->mview->showHydrogens = (RTEST(argv[0]) != 0);
8275 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8277 return (mol->mview->showHydrogens ? Qtrue : Qfalse);
8283 * show_dummy_atoms(bool)
8284 * show_dummy_atoms = bool
8286 * Set the flag whether to show the dummy atoms. If no argument is given, the
8287 * current flag is returned.
8290 s_Molecule_ShowDummyAtoms(int argc, VALUE *argv, VALUE self)
8293 Data_Get_Struct(self, Molecule, mol);
8294 if (mol->mview == NULL)
8297 mol->mview->showDummyAtoms = (RTEST(argv[0]) != 0);
8298 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8300 return (mol->mview->showDummyAtoms ? Qtrue : Qfalse);
8306 * show_expanded(bool)
8307 * show_expanded = bool
8309 * Set the flag whether to show the expanded atoms. If no argument is given, the
8310 * current flag is returned.
8313 s_Molecule_ShowExpanded(int argc, VALUE *argv, VALUE self)
8316 Data_Get_Struct(self, Molecule, mol);
8317 if (mol->mview == NULL)
8320 mol->mview->showExpandedAtoms = (RTEST(argv[0]) != 0);
8321 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8323 return (mol->mview->showExpandedAtoms ? Qtrue : Qfalse);
8329 * show_ellipsoids(bool)
8330 * show_ellipsoids = bool
8332 * Set the flag whether to show the thermal ellipsoids. If no argument is given, the
8333 * current flag is returned.
8336 s_Molecule_ShowEllipsoids(int argc, VALUE *argv, VALUE self)
8339 Data_Get_Struct(self, Molecule, mol);
8340 if (mol->mview == NULL)
8343 mol->mview->showEllipsoids = (RTEST(argv[0]) != 0);
8344 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8346 return (mol->mview->showEllipsoids ? Qtrue : Qfalse);
8351 * is_atom_visible(index) -> Boolean
8353 * Check is an atom is visible. It examines the atom attribute (ap->exflags & kAtomHiddenFlag)
8354 * as well as the molecule attributes (showHydrogens, etc.)
8357 s_Molecule_IsAtomVisible(VALUE self, VALUE ival)
8362 Data_Get_Struct(self, Molecule, mol);
8363 idx = s_Molecule_AtomIndexFromValue(mol, ival);
8364 if (idx < 0 || idx >= mol->natoms)
8366 ap = ATOM_AT_INDEX(mol->atoms, idx);
8367 if (mol->mview != NULL) {
8368 if (mol->mview->showHydrogens == 0 && ap->atomicNumber == 1)
8370 if (mol->mview->showExpandedAtoms == 0 && SYMOP_ALIVE(ap->symop))
8372 if (mol->mview->showDummyAtoms == 0 && ap->atomicNumber == 0)
8375 return ((ap->exflags & kAtomHiddenFlag) != 0 ? Qfalse : Qtrue);
8380 * show_graphite -> Integer
8381 * show_graphite = Integer
8382 * show_graphite = boolean
8384 * Set whether to show the graphite plane. If the argument is positive, it also indicates the
8385 * number of rings to display for each direction.
8386 * If the argument is boolean, only the show/hide flag is set.
8389 s_Molecule_ShowGraphite(int argc, VALUE *argv, VALUE self)
8392 Data_Get_Struct(self, Molecule, mol);
8393 if (mol->mview == NULL)
8396 if (argv[0] == Qnil || argv[0] == Qfalse)
8397 mol->mview->showGraphiteFlag = 0;
8398 else if (argv[0] == Qtrue)
8399 mol->mview->showGraphiteFlag = 1;
8401 int n = NUM2INT(rb_Integer(argv[0]));
8403 rb_raise(rb_eMolbyError, "The argument must be non-negative integer");
8404 mol->mview->showGraphite = n;
8406 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8408 return INT2NUM(mol->mview->showGraphite);
8413 * show_graphite? -> boolean
8415 * Return whether the graphite is set visible or not.
8418 s_Molecule_ShowGraphiteFlag(VALUE self)
8421 Data_Get_Struct(self, Molecule, mol);
8422 if (mol->mview == NULL)
8424 return (mol->mview->showGraphiteFlag ? Qtrue : Qfalse);
8429 * show_periodic_image -> [amin, amax, bmin, bmax, cmin, cmax]
8430 * show_periodic_image = [amin, amax, bmin, bmax, cmin, cmax]
8431 * show_periodic_image = boolean
8433 * Set to show the periodic image of the atoms. If the unit cell is not defined, the values are
8434 * set but no visual effects are observed.
8435 * If the argument is boolean, only the show/hide flag is modified.
8438 s_Molecule_ShowPeriodicImage(int argc, VALUE *argv, VALUE self)
8444 Data_Get_Struct(self, Molecule, mol);
8445 if (mol->mview == NULL)
8447 rb_scan_args(argc, argv, "01", &val);
8449 /* Change current settings */
8450 if (val == Qnil || val == Qfalse)
8451 mol->mview->showPeriodicImageFlag = 0;
8452 else if (val == Qtrue)
8453 mol->mview->showPeriodicImageFlag = 1;
8455 val = rb_ary_to_ary(val);
8456 for (i = 0; i < 6; i++) {
8457 if (i < RARRAY_LEN(val))
8458 ival[i] = NUM2INT(rb_Integer(RARRAY_PTR(val)[i]));
8460 if (ival[0] > 0 || ival[1] < 0 || ival[2] > 0 || ival[3] < 0 || ival[4] > 0 || ival[5] < 0)
8461 rb_raise(rb_eMolbyError, "bad arguments");
8462 for (i = 0; i < 6; i++)
8463 mol->mview->showPeriodicImage[i] = ival[i];
8465 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8468 for (i = 0; i < 6; i++)
8469 rb_ary_push(val, INT2NUM(mol->mview->showPeriodicImage[i]));
8475 * show_periodic_image? -> boolean
8477 * Return whether the periodic images are set to visible or not. This flag is
8478 * independent from the show_periodic_image settings.
8481 s_Molecule_ShowPeriodicImageFlag(VALUE self)
8484 Data_Get_Struct(self, Molecule, mol);
8485 if (mol->mview == NULL)
8487 return (mol->mview->showPeriodicImageFlag ? Qtrue : Qfalse);
8496 * Set the flag whether to draw the model in line mode. If no argument is given, the
8497 * current flag is returned.
8500 s_Molecule_LineMode(int argc, VALUE *argv, VALUE self)
8503 Data_Get_Struct(self, Molecule, mol);
8504 if (mol->mview == NULL)
8507 mol->mview->lineMode = (RTEST(argv[0]) != 0);
8508 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8510 return (mol->mview->lineMode ? Qtrue : Qfalse);
8517 * Resize the model drawing to fit in the window.
8520 s_Molecule_ResizeToFit(VALUE self)
8523 Data_Get_Struct(self, Molecule, mol);
8524 if (mol->mview != NULL)
8525 MainView_resizeToFit(mol->mview);
8531 * get_view_rotation -> [[ax, ay, az], angle]
8533 * Get the current rotation for the view. Angle is in degree, not radian.
8536 s_Molecule_GetViewRotation(VALUE self)
8541 Data_Get_Struct(self, Molecule, mol);
8542 if (mol->mview == NULL)
8544 TrackballGetRotate(mol->mview->track, f);
8545 f[0] = -f[0]; /* Convert to left-handed screw (to be consistent with Transform) */
8549 return rb_ary_new3(2, ValueFromVector(&v), rb_float_new(f[0]));
8554 * get_view_scale -> float
8556 * Get the current scale for the view.
8559 s_Molecule_GetViewScale(VALUE self)
8562 Data_Get_Struct(self, Molecule, mol);
8563 if (mol->mview == NULL)
8565 return rb_float_new(TrackballGetScale(mol->mview->track));
8570 * get_view_center -> Vector
8572 * Get the current center point of the view.
8575 s_Molecule_GetViewCenter(VALUE self)
8580 Data_Get_Struct(self, Molecule, mol);
8581 if (mol->mview == NULL)
8583 TrackballGetTranslate(mol->mview->track, f);
8584 v.x = -f[0] * mol->mview->dimension;
8585 v.y = -f[1] * mol->mview->dimension;
8586 v.z = -f[2] * mol->mview->dimension;
8587 return ValueFromVector(&v);
8592 * set_view_rotation([ax, ay, az], angle) -> self
8594 * Set the current rotation for the view. Angle is in degree, not radian.
8597 s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
8602 Data_Get_Struct(self, Molecule, mol);
8603 if (mol->mview == NULL)
8605 VectorFromValue(aval, &v);
8606 if (NormalizeVec(&v, &v))
8607 rb_raise(rb_eMolbyError, "Cannot normalize nearly zero vector");
8611 f[0] = -NUM2DBL(rb_Float(angval));
8612 TrackballSetRotate(mol->mview->track, f);
8613 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8619 * set_view_scale(scale) -> self
8621 * Set the current scale for the view.
8624 s_Molecule_SetViewScale(VALUE self, VALUE aval)
8627 Data_Get_Struct(self, Molecule, mol);
8628 if (mol->mview == NULL)
8630 TrackballSetScale(mol->mview->track, NUM2DBL(rb_Float(aval)));
8631 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8637 * set_view_center(vec) -> self
8639 * Set the current center point of the view.
8642 s_Molecule_SetViewCenter(VALUE self, VALUE aval)
8647 Data_Get_Struct(self, Molecule, mol);
8648 if (mol->mview == NULL)
8650 VectorFromValue(aval, &v);
8651 f[0] = -v.x / mol->mview->dimension;
8652 f[1] = -v.y / mol->mview->dimension;
8653 f[2] = -v.z / mol->mview->dimension;
8654 TrackballSetTranslate(mol->mview->track, f);
8655 MainViewCallback_setNeedsDisplay(mol->mview, 1);
8661 * set_background_color(red, green, blue)
8663 * Set the background color of the model window.
8666 s_Molecule_SetBackgroundColor(int argc, VALUE *argv, VALUE self)
8669 Data_Get_Struct(self, Molecule, mol);
8670 if (mol->mview != NULL) {
8671 VALUE rval, gval, bval;
8672 rb_scan_args(argc, argv, "30", &rval, &gval, &bval);
8673 MainView_setBackgroundColor(mol->mview, NUM2DBL(rb_Float(rval)), NUM2DBL(rb_Float(gval)), NUM2DBL(rb_Float(bval)));
8680 * create_graphic(kind, color, points, fill = nil) -> integer
8682 * Create a new graphic object.
8683 * kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
8684 * color: an array of 3 (rgb) or 4 (rgba) floating numbers
8685 * points: an array of Vectors
8689 s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
8695 VALUE kval, cval, pval, fval;
8696 Data_Get_Struct(self, Molecule, mol);
8697 if (mol->mview == NULL)
8698 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8699 rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
8700 kval = rb_obj_as_string(kval);
8701 memset(&g, 0, sizeof(g));
8703 p = RSTRING_PTR(kval);
8704 if (strcmp(p, "line") == 0)
8705 g.kind = kMainViewGraphicLine;
8706 else if (strcmp(p, "poly") == 0)
8707 g.kind = kMainViewGraphicPoly;
8708 else if (strcmp(p, "cylinder") == 0)
8709 g.kind = kMainViewGraphicCylinder;
8710 else if (strcmp(p, "cone") == 0)
8711 g.kind = kMainViewGraphicCone;
8712 else if (strcmp(p, "ellipsoid") == 0)
8713 g.kind = kMainViewGraphicEllipsoid;
8714 else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
8715 g.closed = (RTEST(fval) ? 1 : 0);
8716 cval = rb_ary_to_ary(cval);
8717 n = RARRAY_LEN(cval);
8718 if (n < 3 || n >= 5)
8719 rb_raise(rb_eArgError, "the color should have 3 or 4 elements");
8722 for (i = 0; i < n; i++)
8723 g.rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
8724 pval = rb_ary_to_ary(pval);
8725 n = RARRAY_LEN(pval);
8726 ni = -1; /* If this is non-negative, then ni-th control point is [number, 0, 0] */
8728 rb_raise(rb_eArgError, "no control points are given");
8730 case kMainViewGraphicLine:
8732 rb_raise(rb_eArgError, "the line object must have at least two control points");
8734 case kMainViewGraphicPoly:
8736 rb_raise(rb_eArgError, "the polygon object must have at least three control points");
8738 case kMainViewGraphicCylinder:
8739 case kMainViewGraphicCone:
8741 rb_raise(rb_eArgError, "the %s object must have two control points and one number (radius)", (g.kind == kMainViewGraphicCylinder ? "cylinder" : "cone"));
8744 case kMainViewGraphicEllipsoid:
8748 rb_raise(rb_eArgError, "the ellipsoid object must have either one point and one number (radius) or four points (center and three main axes)");
8751 NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
8752 for (i = 0; i < n; i++) {
8755 v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
8758 VectorFromValue(RARRAY_PTR(pval)[i], &v);
8760 g.points[i * 3] = v.x;
8761 g.points[i * 3 + 1] = v.y;
8762 g.points[i * 3 + 2] = v.z;
8764 if (g.kind == kMainViewGraphicEllipsoid && ni == 1) {
8766 AssignArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, 4, NULL);
8767 g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
8768 g.points[7] = g.points[11] = g.points[3];
8770 MainView_insertGraphic(mol->mview, -1, &g);
8771 return INT2NUM(mol->mview->ngraphics - 1);
8776 * remove_graphic(index) -> integer
8778 * Remove a graphic object.
8781 s_Molecule_RemoveGraphic(VALUE self, VALUE ival)
8785 Data_Get_Struct(self, Molecule, mol);
8786 if (mol->mview == NULL)
8787 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8788 i = NUM2INT(rb_Integer(ival));
8789 if (i < 0 || i >= mol->mview->ngraphics)
8790 rb_raise(rb_eArgError, "graphic index is out of range");
8791 MainView_removeGraphic(mol->mview, i);
8797 * ngraphics -> integer
8799 * Get the number of graphic objects.
8802 s_Molecule_NGraphics(VALUE self)
8805 Data_Get_Struct(self, Molecule, mol);
8806 if (mol->mview == NULL)
8807 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8808 return INT2NUM(mol->mview->ngraphics);
8813 * set_graphic_point(graphic_index, point_index, new_value) -> new_value
8815 * Change the point_index-th control point of graphic_index-th graphic object
8819 s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
8821 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 index = NUM2INT(rb_Integer(pval));
8833 if (index < 0 || index >= gp->npoints)
8834 rb_raise(rb_eArgError, "the point index is out of range");
8835 if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
8836 if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
8837 v.x = NUM2DBL(rb_Float(nval));
8839 } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
8840 gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
8841 gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
8843 } else rb_raise(rb_eArgError, "the argument must be an array-like object");
8846 v.x = kInvalidFloat;
8848 } else VectorFromValue(nval, &v);
8850 gp->points[index * 3] = v.x;
8851 gp->points[index * 3 + 1] = v.y;
8852 gp->points[index * 3 + 2] = v.z;
8853 MoleculeCallback_notifyModification(mol, 0);
8859 * set_graphic_color(graphic_index, new_value) -> new_value
8861 * Change the color of graphic_index-th graphic object
8865 s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
8867 MainViewGraphic *gp;
8870 Data_Get_Struct(self, Molecule, mol);
8871 if (mol->mview == NULL)
8872 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8873 index = NUM2INT(rb_Integer(gval));
8874 if (index < 0 || index >= mol->mview->ngraphics)
8875 rb_raise(rb_eArgError, "the graphic index is out of range");
8876 gp = mol->mview->graphics + index;
8877 cval = rb_ary_to_ary(cval);
8878 n = RARRAY_LEN(cval);
8879 if (n != 3 && n != 4)
8880 rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
8881 for (index = 0; index < n; index++) {
8882 gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
8886 MoleculeCallback_notifyModification(mol, 0);
8892 * show_graphic(graphic_index) -> self
8894 * Enable the visible flag of the graphic_index-th graphic object
8898 s_Molecule_ShowGraphic(VALUE self, VALUE gval)
8900 MainViewGraphic *gp;
8903 Data_Get_Struct(self, Molecule, mol);
8904 if (mol->mview == NULL)
8905 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8906 index = NUM2INT(rb_Integer(gval));
8907 if (index < 0 || index >= mol->mview->ngraphics)
8908 rb_raise(rb_eArgError, "the graphic index is out of range");
8909 gp = mol->mview->graphics + index;
8911 MoleculeCallback_notifyModification(mol, 0);
8917 * hide_graphic(graphic_index) -> self
8919 * Disable the visible flag of the graphic_index-th graphic object
8923 s_Molecule_HideGraphic(VALUE self, VALUE gval)
8925 MainViewGraphic *gp;
8928 Data_Get_Struct(self, Molecule, mol);
8929 if (mol->mview == NULL)
8930 rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
8931 index = NUM2INT(rb_Integer(gval));
8932 if (index < 0 || index >= mol->mview->ngraphics)
8933 rb_raise(rb_eArgError, "the graphic index is out of range");
8934 gp = mol->mview->graphics + index;
8936 MoleculeCallback_notifyModification(mol, 0);
8944 * Show the string in the info text box.
8947 s_Molecule_ShowText(VALUE self, VALUE arg)
8950 Data_Get_Struct(self, Molecule, mol);
8951 if (mol->mview != NULL)
8952 MainViewCallback_drawInfoText(mol->mview, StringValuePtr(arg));
8958 * md_arena -> MDArena
8960 * Returns the MDArena object associated to this molecule. If no MDArena is associated to
8961 * this molecule, a new arena is created.
8964 s_Molecule_MDArena(VALUE self)
8968 Data_Get_Struct(self, Molecule, mol);
8969 if (mol->arena == NULL)
8971 retval = ValueFromMDArena(mol->arena);
8977 * set_parameter_attr(type, index, key, value, src) -> value
8979 * This method is used only internally.
8982 s_Molecule_SetParameterAttr(VALUE self, VALUE tval, VALUE ival, VALUE kval, VALUE vval, VALUE sval)
8984 /* This method is called from MolAction to change a MM parameter attribute. */
8989 Data_Get_Struct(self, Molecule, mol);
8990 pval = ValueFromMoleculeWithParameterTypeAndIndex(mol, FIX2INT(tval), NUM2INT(ival));
8991 vval = s_ParameterRef_SetAttr(pval, kval, vval);
8993 /* This is the special part of this method; it allows modification of the src field. */
8994 /* (ParameterRef#set_attr sets 0 to the src field) */
8995 Data_Get_Struct(pval, ParameterRef, pref);
8996 up = ParameterRefGetPar(pref);
8997 up->bond.src = FIX2INT(sval);
9004 * parameter -> Parameter
9006 * Get the local parameter of this molecule. If not defined, returns nil.
9009 s_Molecule_Parameter(VALUE self)
9012 Data_Get_Struct(self, Molecule, mol);
9013 /* if (mol->par == NULL)
9015 return s_NewParameterValueFromValue(self);
9020 * selectedMO -> IntGroup
9022 * Returns a group of selected mo in the "MO Info" table. If the MO info table
9023 * is not selected, returns nil. If the MO info table is selected but no MOs
9024 * are selected, returns an empty IntGroup. The numbers in the table are 1-based.
9027 s_Molecule_SelectedMO(VALUE self)
9032 Data_Get_Struct(self, Molecule, mol);
9033 if (mol->mview == NULL)
9035 ig = MainView_selectedMO(mol->mview);
9038 IntGroupOffset(ig, 1);
9039 val = ValueFromIntGroup(ig);
9040 IntGroupRelease(ig);
9046 * default_MO_grid(npoints = 80*80*80) -> [origin, dx, dy, dz, nx, ny, nz]
9048 * Returns a default MO grid for cube file generation. Origin: Vector, dx, dy, dz: float, nx, ny, nz: integer.
9049 * If the molecule does not contain a basis set information, then returns nil.
9052 s_Molecule_GetDefaultMOGrid(int argc, VALUE *argv, VALUE self)
9055 Vector o, dx, dy, dz;
9058 Int npoints = 80 * 80 * 80;
9059 Data_Get_Struct(self, Molecule, mol);
9060 if (mol->bset == NULL)
9062 rb_scan_args(argc, argv, "01", &nval);
9064 npoints = NUM2INT(rb_Integer(nval));
9065 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9067 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));
9071 s_Cubegen_callback(double progress, void *ref)
9073 MyAppCallback_setProgressValue(progress);
9074 if (MyAppCallback_checkInterrupt())
9081 * cubegen(fname, mo, npoints=1000000 [, iflag [, beta]])
9082 * cubegen(fname, mo, origin, dx, dy, dz, nx, ny, nz [, iflag [, beta]])
9084 * Calculate the molecular orbital with number mo and create a 'cube' file.
9085 * In the first form, the cube size is estimated from the atomic coordinates. In the
9086 * second form, the cube dimension is explicitly given.
9087 * Returns fname when successful, nil otherwise.
9088 * If iflag is non-false, then MyAppCallback_checkInterrupt() is periodically called during calculation.
9089 * If beta is non-false, then the beta MO is calculated (valid only for UHF MO)
9090 * (The interrupt monitoring thread is disabled, so as not to enter Ruby interpreter during calculation)
9093 s_Molecule_Cubegen(int argc, VALUE *argv, VALUE self)
9095 VALUE fval, mval, oval, dxval, dyval, dzval, nxval, nyval, nzval, ival, bval;
9097 Int mono, nx, ny, nz, npoints;
9098 Vector o, dx, dy, dz;
9101 Data_Get_Struct(self, Molecule, mol);
9102 if (mol->bset == NULL)
9103 rb_raise(rb_eMolbyError, "The molecule does not contain MO information");
9104 rb_scan_args(argc, argv, "29", &fval, &mval, &oval, &dxval, &dyval, &dzval, &nxval, &nyval, &nzval, &ival, &bval);
9106 /* Set up parameters */
9107 mono = NUM2INT(rb_Integer(mval));
9108 if (mono <= 0 || mono > mol->bset->ncomps)
9109 rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
9111 if (mol->bset->rflag != 0)
9112 rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
9113 mono += mol->bset->ncomps;
9116 if (oval == Qnil || dxval == Qnil || dyval == Qnil) {
9117 /* Automatic grid formation */
9119 npoints = NUM2INT(rb_Integer(oval));
9123 else if (npoints < 8)
9124 rb_raise(rb_eMolbyError, "The number of points (%d) should be at least 8", npoints);
9125 if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
9126 rb_raise(rb_eMolbyError, "Cannot determine cube grids");
9130 VectorFromValue(oval, &o);
9131 if (TYPE(dxval) == T_ARRAY || rb_obj_is_kind_of(dxval, rb_cVector3D))
9132 VectorFromValue(dxval, &dx);
9134 dx.x = NUM2DBL(rb_Float(dxval));
9137 if (TYPE(dyval) == T_ARRAY || rb_obj_is_kind_of(dyval, rb_cVector3D))
9138 VectorFromValue(dyval, &dy);
9140 dy.y = NUM2DBL(rb_Float(dyval));
9143 if (TYPE(dzval) == T_ARRAY || rb_obj_is_kind_of(dzval, rb_cVector3D))
9144 VectorFromValue(dzval, &dz);
9146 dz.z = NUM2DBL(rb_Float(dzval));
9149 nx = NUM2INT(rb_Integer(nxval));
9150 ny = NUM2INT(rb_Integer(nyval));
9151 nz = NUM2INT(rb_Integer(nzval));
9152 if (nx <= 0 || ny <= 0 || nz <= 0)
9153 rb_raise(rb_eMolbyError, "The number of points (%d,%d,%d) should be positive integers", nx, ny, nz);
9154 else if (nx > 10000 || ny > 10000 || nz > 10000 || nx * ny * nz > 100000000)
9155 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);
9159 index = MoleculeCalcMO(mol, mono, &o, &dx, &dy, &dz, nx, ny, nz, (RTEST(ival) ? s_Cubegen_callback : NULL), NULL);
9163 rb_raise(rb_eMolbyError, "Cannot calculate MO %d", mono);
9165 /* Output to file */
9166 MoleculeCallback_displayName(mol, buf, sizeof buf);
9167 n = MoleculeOutputCube(mol, index, FileStringValuePtr(fval), buf);
9169 rb_raise(rb_eMolbyError, "Cannot output MO cube to file %p", FileStringValuePtr(fval));
9171 /* Discard the cube */
9172 MoleculeClearCubeAtIndex(mol, index);
9180 * Get the number of electrostatic potential info.
9183 s_Molecule_NElpots(VALUE self)
9186 Data_Get_Struct(self, Molecule, mol);
9187 return INT2NUM(mol->nelpots);
9194 * Get the electrostatic potential info at the given index. If present, then the
9195 * return value is [Vector, Float] (position and potential). If not present, then
9199 s_Molecule_Elpot(VALUE self, VALUE ival)
9203 Data_Get_Struct(self, Molecule, mol);
9204 idx = NUM2INT(rb_Integer(ival));
9205 if (idx < 0 || idx >= mol->nelpots)
9207 return rb_ary_new3(2, ValueFromVector(&mol->elpots[idx].pos), rb_float_new(mol->elpots[idx].esp));
9212 * add_gaussian_orbital_shell(sym, nprims, atom_index)
9214 * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
9215 * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
9219 s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
9222 int sym, nprims, a_idx, n;
9223 Data_Get_Struct(self, Molecule, mol);
9224 sym = NUM2INT(rb_Integer(symval));
9225 nprims = NUM2INT(rb_Integer(npval));
9226 a_idx = NUM2INT(rb_Integer(aval));
9227 n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
9229 rb_raise(rb_eMolbyError, "Molecule is emptry");
9231 rb_raise(rb_eMolbyError, "Low memory");
9233 rb_raise(rb_eMolbyError, "Unknown orbital type");
9235 rb_raise(rb_eMolbyError, "Unknown error");
9241 * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
9243 * To be used internally. Add a gaussian primitive coefficients.
9246 s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
9250 Double exponent, contraction, contraction_sp;
9251 Data_Get_Struct(self, Molecule, mol);
9252 exponent = NUM2DBL(rb_Float(expval));
9253 contraction = NUM2DBL(rb_Float(cval));
9254 contraction_sp = NUM2DBL(rb_Float(cspval));
9255 n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
9257 rb_raise(rb_eMolbyError, "Molecule is emptry");
9259 rb_raise(rb_eMolbyError, "Low memory");
9261 rb_raise(rb_eMolbyError, "Unknown error");
9269 * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
9272 s_Molecule_MOType(VALUE self)
9275 Data_Get_Struct(self, Molecule, mol);
9276 if (mol != NULL && mol->bset != NULL) {
9278 int rflag = mol->bset->rflag;
9281 else if (rflag == 2)
9284 return rb_str_new2(s);
9290 * set_mo_coefficients(idx, energy, coefficients)
9292 * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
9293 * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
9294 * of MO coefficients.
9297 s_Molecule_SetMOCoefficients(VALUE self, VALUE ival, VALUE eval, VALUE aval)
9303 Data_Get_Struct(self, Molecule, mol);
9304 idx = NUM2INT(rb_Integer(ival));
9305 energy = NUM2DBL(rb_Float(eval));
9306 aval = rb_ary_to_ary(aval);
9307 ncomps = RARRAY_LEN(aval);
9308 coeffs = (Double *)calloc(sizeof(Double), ncomps);
9309 if (coeffs == NULL) {
9313 for (i = 0; i < ncomps; i++)
9314 coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
9315 i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
9318 rb_raise(rb_eMolbyError, "Molecule is emptry");
9320 rb_raise(rb_eMolbyError, "Low memory");
9322 rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
9324 rb_raise(rb_eMolbyError, "Bad MO index");
9326 rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
9328 rb_raise(rb_eMolbyError, "Unknown error");
9334 * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
9336 * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
9337 * ne_alpha, ne_beta: number of alpha/beta electrons.
9340 s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
9343 Int rflag, na, nb, n;
9344 Data_Get_Struct(self, Molecule, mol);
9345 rflag = NUM2INT(rb_Integer(rval));
9346 na = NUM2INT(rb_Integer(naval));
9347 nb = NUM2INT(rb_Integer(nbval));
9348 n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
9350 rb_raise(rb_eMolbyError, "Molecule is emptry");
9352 rb_raise(rb_eMolbyError, "Low memory");
9354 rb_raise(rb_eMolbyError, "Unknown error");
9360 * search_equivalent_atoms(ig = nil)
9362 * Search equivalent atoms (within the atom group if given). Returns an array of integers.
9365 s_Molecule_SearchEquivalentAtoms(int argc, VALUE *argv, VALUE self)
9371 Data_Get_Struct(self, Molecule, mol);
9372 if (mol->natoms == 0)
9374 rb_scan_args(argc, argv, "01", &val);
9376 ig = IntGroupFromValue(val);
9378 result = MoleculeSearchEquivalentAtoms(mol, ig);
9380 rb_raise(rb_eMolbyError, "Failed to search equivalent atoms (out of memory?)");
9382 IntGroupRelease(ig);
9383 val = rb_ary_new2(mol->natoms);
9384 for (i = 0; i < mol->natoms; i++)
9385 rb_ary_push(val, INT2NUM(result[i]));
9392 * create_pi_anchor(name, type, group [, weights]) -> index
9393 * insert_pi_anchor(index, name, type, group [, weights]) -> index
9394 * replace_pi_anchor(index, name, type, group [, weights]) -> index
9396 * Create or replace a "pi anchor", which is an anchor point to define a metal-pi bond.
9397 * If index is negative or no less than the current number of pi anchors, create
9398 * a new anchor. Otherwise, replace the existing anchor.
9399 * Name and type are Strings, and are similar to those in atoms.
9400 * Group is a group of atoms to define a pi system to be bound to the metal.
9401 * Weights (optional) is an Array of Floats, which determine the significance
9402 * of the component atoms. If not given, then 1.0/N (N is the number of atoms
9403 * in the group) is assumed for all atoms.
9406 s_Molecule_CreateOrReplacePiAnchor(int func, int argc, VALUE *argv, VALUE self)
9409 Int idx, i, j, atype, *ip;
9410 VALUE ival, nval, tval, gval, wval;
9411 char *np, *tp, aname[6];
9414 Data_Get_Struct(self, Molecule, mol);
9415 if (func == 0) { /* create */
9416 rb_scan_args(argc, argv, "31", &nval, &tval, &gval, &wval);
9417 idx = mol->npiatoms;
9419 rb_scan_args(argc, argv, "41", &ival, &nval, &tval, &gval, &wval);
9420 idx = NUM2INT(rb_Integer(ival));
9422 idx = mol->npiatoms;
9423 if (idx > mol->npiatoms || (func == 2 && idx == mol->npiatoms))
9424 rb_raise(rb_eMolbyError, "pi anchor index out of range");
9426 np = StringValuePtr(nval);
9427 strncpy(aname, np, 4);
9428 tp = StringValuePtr(tval);
9429 atype = AtomTypeEncodeToUInt(tp);
9430 ig = IntGroupFromValue(gval);
9431 if ((i = IntGroupGetCount(ig)) < 2)
9432 rb_raise(rb_eMolbyError, "too few atoms are given (at least 2 are needed)");
9433 ip = (Int *)malloc(sizeof(Int) * i);
9434 for (i = 0; (j = IntGroupGetNthPoint(ig, i)) >= 0; i++) {
9436 if (j < 0 || j >= mol->natoms) {
9438 rb_raise(rb_eMolbyError, "the atom index (%d) is out of range", j);
9441 dp = (Double *)malloc(sizeof(Double) * i);
9443 wval = rb_ary_to_ary(wval);
9444 if (RARRAY_LEN(wval) < i) {
9447 rb_raise(rb_eMolbyError, "the number of floats is not sufficient (%d needed but only %d given)", i, RARRAY_LEN(wval));
9449 for (j = 0; j < i; j++) {
9450 dp[j] = NUM2DBL(rb_Float(RARRAY_PTR(wval)[j]));
9453 for (j = 0; j < i; j++) {
9457 if (func == 0 || func == 1) /* Create or insert */
9458 MolActionCreateAndPerform(mol, gMolActionInsertOnePiAtom, idx, 4, aname, atype, i, ip, i, dp);
9460 MolActionCreateAndPerform(mol, gMolActionReplaceOnePiAtom, idx, 4, aname, atype, i, ip, i, dp);
9463 return INT2NUM(idx);
9467 s_Molecule_CreatePiAnchor(int argc, VALUE *argv, VALUE self)
9469 return s_Molecule_CreateOrReplacePiAnchor(0, argc, argv, self);
9473 s_Molecule_InsertPiAnchor(int argc, VALUE *argv, VALUE self)
9475 return s_Molecule_CreateOrReplacePiAnchor(1, argc, argv, self);
9479 s_Molecule_ReplacePiAnchor(int argc, VALUE *argv, VALUE self)
9481 return s_Molecule_CreateOrReplacePiAnchor(2, argc, argv, self);
9486 * remove_pi_anchor(idx) -> Molecule
9488 * Remove the pi-anchor at the given index.
9491 s_Molecule_RemovePiAnchor(VALUE self, VALUE ival)
9494 Int idx = NUM2INT(rb_Integer(ival));
9495 Data_Get_Struct(self, Molecule, mol);
9496 if (idx < 0 || idx >= mol->npiatoms)
9497 rb_raise(rb_eMolbyError, "pi-anchor index (%d) is out of range (should be 0 <= index < %d)", idx, mol->npiatoms);
9498 MolActionCreateAndPerform(mol, gMolActionRemoveOnePiAtom, idx);
9504 * pi_anchor(idx) -> nil or [name, type, group, weights]
9506 * Return the attributes of the idx-th pi anchor if present, otherwise nil.
9509 s_Molecule_PiAnchorAtIndex(VALUE self, VALUE ival)
9513 char aname[6], atype[8];
9515 VALUE cval, wval, rval;
9516 Int idx = NUM2INT(rb_Integer(ival));
9517 Data_Get_Struct(self, Molecule, mol);
9518 if (idx < 0 || idx >= mol->npiatoms)
9520 pp = mol->piatoms + idx;
9521 strncpy(aname, pp->aname, 4);
9523 AtomTypeDecodeToString(pp->type, atype);
9524 cval = rb_ary_new2(pp->connect.count);
9525 connects = AtomConnectData(&pp->connect);
9526 for (i = 0; i < pp->connect.count; i++)
9527 rb_ary_store(cval, i, INT2NUM(connects[i]));
9528 wval = rb_ary_new2(pp->ncoeffs);
9529 for (i = 0; i < pp->ncoeffs; i++)
9530 rb_ary_store(wval, i, rb_float_new(pp->coeffs[i]));
9531 rval = rb_ary_new3(4, rb_str_new2(aname), rb_str_new2(atype), cval, wval);
9537 * pi_anchor_r(idx) -> Vector3D
9539 * Calculate the cartesian coordinate of the idx-th pi anchor if present.
9542 s_Molecule_PiAnchorR(VALUE self, VALUE ival)
9546 Int idx = NUM2INT(rb_Integer(ival));
9547 Data_Get_Struct(self, Molecule, mol);
9548 if (idx < 0 || idx >= mol->npiatoms)
9549 rb_raise(rb_eMolbyError, "no pi anchor present: %d", idx);
9550 MoleculeCalculatePiAtomPosition(mol, idx, &v);
9551 return ValueFromVector(&v);
9556 * pi_anchor_fract_r(idx) -> Vector3D
9558 * Calculate the fractional coordinate of the idx-th pi anchor if present.
9561 s_Molecule_PiAnchorFractR(VALUE self, VALUE ival)
9565 Int idx = NUM2INT(rb_Integer(ival));
9566 Data_Get_Struct(self, Molecule, mol);
9567 if (idx < 0 || idx >= mol->npiatoms)
9568 rb_raise(rb_eMolbyError, "no pi anchor present: %d", idx);
9569 MoleculeCalculatePiAtomPosition(mol, idx, &v);
9570 if (mol->cell != NULL)
9571 TransformVec(&v, mol->cell->rtr, &v);
9572 return ValueFromVector(&v);
9577 * count_pi_anchors -> Integer
9579 * Return the number of defined pi anchors.
9582 s_Molecule_CountPiAnchors(VALUE self)
9585 Data_Get_Struct(self, Molecule, mol);
9586 return INT2NUM(mol->npiatoms);
9591 * create_pi_anchor_construct(n1, n2, n3 = nil, n4 = nil) -> Integer
9593 * Create a bond, angle, or dihedral including one or more pi anchor points.
9594 * The arguments can either be an atom representation (atom index, atom name, res_seq:name)
9595 * or a pi-anchor representation (pi-anchor index, pi-anchor name).
9596 * The pi-anchor index is represented by a negative integer -N, which corresponds to
9597 * the (N-1)-th pi anchor.
9598 * Returns the index for the newly created construct.
9599 * This operation is undoable.
9602 s_Molecule_CreatePiAnchorConstruct(int argc, VALUE *argv, VALUE self)
9608 Data_Get_Struct(self, Molecule, mol);
9609 rb_scan_args(argc, argv, "22", vals, vals + 1, vals + 2, vals + 3);
9610 for (i = 0; i < 4; i++) {
9611 if (vals[i] == Qnil)
9613 else ivals[i] = s_Molecule_AtomOrPiAtomIndexFromValue(mol, vals[i]);
9615 for (i = 0; i < 4; i++) {
9616 if (ivals[i] >= ATOMS_MAX_NUMBER)
9620 rb_raise(rb_eMolbyError, "No pi anchor is specified");
9621 ig = IntGroupNewWithPoints(mol->npibonds, 1, -1);
9622 MolActionCreateAndPerform(mol, gMolActionInsertPiBonds, ig, 4, ivals);
9623 IntGroupRelease(ig);
9624 return INT2NUM(mol->npibonds);
9629 * remove_pi_anchor_constructs(IntGroup) -> self
9631 * Remove pi anchor constructs (bond, angle, dihedral) with indices specified in IntGroup.
9632 * This operation is undoable.
9635 s_Molecule_RemovePiAnchorConstructs(VALUE self, VALUE igval)
9639 Data_Get_Struct(self, Molecule, mol);
9640 ig = IntGroupFromValue(igval);
9641 MolActionCreateAndPerform(mol, gMolActionRemovePiBonds, ig);
9642 IntGroupRelease(ig);
9648 * count_pi_anchor_constructs -> Integer
9650 * Returns the number of pi anchor constructs.
9653 s_Molecule_CountPiAnchorConstructs(VALUE self)
9656 Data_Get_Struct(self, Molecule, mol);
9657 return INT2NUM(mol->npibonds);
9662 * pi_anchor_construct(index) -> Array of Integers
9664 * Returns the elements representing the index-th pi anchor constructs.
9667 s_Molecule_PiAnchorConstructAtIndex(VALUE self, VALUE ival)
9672 Data_Get_Struct(self, Molecule, mol);
9673 idx = NUM2INT(rb_Integer(ival));
9674 if (idx < 0 || idx >= mol->npibonds)
9675 rb_raise(rb_eMolbyError, "index of pi anchor constructs out of range (%d)", idx);
9676 for (i = 0; i < 4; i++) {
9677 n = mol->pibonds[idx * 4 + i];
9680 if (n >= ATOMS_MAX_NUMBER)
9681 n = -(n - ATOMS_MAX_NUMBER) - 1;
9682 vals[i] = INT2NUM(n);
9684 return rb_ary_new4(i, vals);
9689 * current -> Molecule
9691 * Get the currently "active" molecule.
9694 s_Molecule_Current(VALUE klass)
9696 return ValueFromMolecule(MoleculeCallback_currentMolecule());
9701 * Molecule[] -> Molecule
9702 * Molecule[n] -> Molecule
9703 * Molecule[name] -> Molecule
9704 * Molecule[name, k] -> Molecule
9705 * Molecule[regex] -> Molecule
9706 * Molecule[regex, k] -> Molecule
9708 * Molecule[] is equivalent to Molecule.current.
9709 * Molecule[n] (n is an integer) is equivalent to Molecule.list[n].
9710 * Molecule[name] gives the first document (in the order of creation time) that has
9711 * the given name. If a second argument (k) is given, the k-th document that has the
9712 * given name is returned.
9713 * Molecule[regex] gives the first document (in the order of creation time) that
9714 * has a name matching the regular expression. If a second argument (k) is given,
9715 * the k-th document that has a name matching the re is returned.
9718 s_Molecule_MoleculeAtIndex(int argc, VALUE *argv, VALUE klass)
9724 rb_scan_args(argc, argv, "02", &val, &kval);
9726 return s_Molecule_Current(klass);
9727 if (rb_obj_is_kind_of(val, rb_cInteger)) {
9729 mol = MoleculeCallback_moleculeAtIndex(idx);
9730 } else if (rb_obj_is_kind_of(val, rb_cString)) {
9731 char *p = StringValuePtr(val);
9732 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9733 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9734 MoleculeCallback_displayName(mol, buf, sizeof buf);
9735 if (strcmp(buf, p) == 0 && --k == 0)
9738 } else if (rb_obj_is_kind_of(val, rb_cRegexp)) {
9739 k = (kval == Qnil ? 1 : NUM2INT(rb_Integer(kval)));
9740 for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
9742 MoleculeCallback_displayName(mol, buf, sizeof buf);
9743 name = rb_str_new2(buf);
9744 if (rb_reg_match(val, name) != Qnil && --k == 0)
9747 } else rb_raise(rb_eTypeError, "the argument should be either empty, an integer, a string, or a regular expression");
9751 else return ValueFromMolecule(mol);
9756 * list -> array of Molecules
9758 * Get the list of molecules associated to the documents, in the order of creation
9759 * time of the document. If no document is open, returns an empry array.
9762 s_Molecule_List(VALUE klass)
9769 while ((mol = MoleculeCallback_moleculeAtIndex(i)) != NULL) {
9770 rb_ary_push(ary, ValueFromMolecule(mol));
9778 * ordered_list -> array of Molecules
9780 * Get the list of molecules associated to the documents, in the order of front-to-back
9781 * ordering of the associated window. If no document is open, returns an empry array.
9784 s_Molecule_OrderedList(VALUE klass)
9791 while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
9792 rb_ary_push(ary, ValueFromMolecule(mol));
9803 /* Define module Molby */
9804 rb_mMolby = rb_define_module("Molby");
9806 /* Define Vector3D, Transform, IntGroup */
9809 /* Define MDArena */
9810 Init_MolbyMDTypes();
9812 /* class Molecule */
9813 rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
9814 rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
9815 rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
9816 rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
9817 rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
9818 rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
9819 rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
9820 rb_define_method(rb_cMolecule, "loadpdb", s_Molecule_Loadpdb, -1);
9821 rb_define_method(rb_cMolecule, "loaddcd", s_Molecule_Loaddcd, -1);
9822 rb_define_method(rb_cMolecule, "loadtep", s_Molecule_Loadtep, -1);
9823 rb_define_method(rb_cMolecule, "loadres", s_Molecule_Loadres, -1);
9824 rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
9825 rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
9826 rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
9827 rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
9828 rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
9829 rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
9830 rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
9831 rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
9832 rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
9833 rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
9834 /* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
9835 rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
9836 rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
9837 rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
9838 rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
9839 rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
9840 rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
9841 rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
9842 rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
9843 rb_define_method(rb_cMolecule, "dihedrals", s_Molecule_Dihedrals, 0);
9844 rb_define_method(rb_cMolecule, "impropers", s_Molecule_Impropers, 0);
9845 rb_define_method(rb_cMolecule, "residues", s_Molecule_Residues, 0);
9846 rb_define_method(rb_cMolecule, "natoms", s_Molecule_Natoms, 0);
9847 rb_define_method(rb_cMolecule, "nbonds", s_Molecule_Nbonds, 0);
9848 rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
9849 rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
9850 rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
9852 rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
9853 rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
9854 rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
9855 rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
9856 rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
9858 rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
9859 rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
9860 rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
9861 rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
9862 rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
9863 rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
9865 rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
9866 rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
9867 rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
9868 rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
9869 rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
9870 rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
9871 rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
9872 rb_define_alias(rb_cMolecule, "set_cell", "cell=");
9873 rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
9874 rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
9875 rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
9876 rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
9877 rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
9878 rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
9879 rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
9880 rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
9881 rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
9882 rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
9883 rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
9884 rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
9885 rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
9886 rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
9887 rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
9888 rb_define_alias(rb_cMolecule, "+", "add");
9889 rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
9890 rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
9891 rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
9892 rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
9893 rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
9894 rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
9895 rb_define_alias(rb_cMolecule, "create_bonds", "create_bond");
9896 rb_define_method(rb_cMolecule, "remove_bond", s_Molecule_RemoveBond, -1);
9897 rb_define_alias(rb_cMolecule, "remove_bonds", "remove_bond");
9898 rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
9899 rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
9900 rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
9901 rb_define_method(rb_cMolecule, "remove_dihedral", s_Molecule_RemoveDihedral, 4);
9902 rb_define_method(rb_cMolecule, "add_improper", s_Molecule_AddImproper, 4);
9903 rb_define_method(rb_cMolecule, "remove_improper", s_Molecule_RemoveImproper, 4);
9904 rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
9905 rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
9906 rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
9907 rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
9908 rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
9909 rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
9910 rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
9911 rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
9912 rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
9913 rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
9914 rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
9915 rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
9916 rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
9917 rb_define_alias(rb_cMolecule, "select_frame", "frame=");
9918 rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
9919 rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
9920 rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
9921 rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
9922 rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
9923 rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
9924 rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
9925 rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
9926 rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
9927 rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
9928 rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
9929 rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
9930 rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
9931 rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
9932 rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
9933 rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, 0);
9934 rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
9935 rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
9936 rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
9937 rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
9938 rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
9939 rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
9940 rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
9941 rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
9942 rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
9943 rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
9944 rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
9945 rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
9946 rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
9947 rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
9948 rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
9949 rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
9950 rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
9951 rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
9952 rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
9953 rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
9954 rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
9955 rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
9956 rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
9957 rb_define_method(rb_cMolecule, "update_enabled=", s_Molecule_SetUpdateEnabled, 1);
9958 rb_define_method(rb_cMolecule, "show_unitcell", s_Molecule_ShowUnitCell, -1);
9959 rb_define_method(rb_cMolecule, "show_hydrogens", s_Molecule_ShowHydrogens, -1);
9960 rb_define_method(rb_cMolecule, "show_hydrogens=", s_Molecule_ShowHydrogens, -1);
9961 rb_define_method(rb_cMolecule, "show_dummy_atoms", s_Molecule_ShowDummyAtoms, -1);
9962 rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
9963 rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
9964 rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
9965 rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
9966 rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
9967 rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
9968 rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
9969 rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
9970 rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
9971 rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
9972 rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
9973 rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
9974 rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
9975 rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
9976 rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
9977 rb_define_alias(rb_cMolecule, "show_hydrogens=", "show_hydrogens");
9978 rb_define_alias(rb_cMolecule, "show_dummy_atoms=", "show_dummy_atoms");
9979 rb_define_alias(rb_cMolecule, "show_expanded=", "show_expanded");
9980 rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
9981 rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
9982 rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
9983 rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
9984 rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
9985 rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
9986 #if 1 || !defined(__CMDMAC__)
9987 rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
9988 rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
9989 rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
9990 rb_define_method(rb_cMolecule, "set_view_rotation", s_Molecule_SetViewRotation, 2);
9991 rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
9992 rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
9993 rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
9994 rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
9995 rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
9996 rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
9997 rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
9998 rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
9999 rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
10000 rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
10002 rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
10003 rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
10004 rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
10005 rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
10006 rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
10007 rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
10008 rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
10009 rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
10010 rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
10011 rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
10012 rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
10013 rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
10014 rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
10015 rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
10016 rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
10018 rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
10019 rb_define_method(rb_cMolecule, "replace_pi_anchor", s_Molecule_ReplacePiAnchor, -1);
10020 rb_define_method(rb_cMolecule, "insert_pi_anchor", s_Molecule_InsertPiAnchor, -1);
10021 rb_define_method(rb_cMolecule, "remove_pi_anchor", s_Molecule_RemovePiAnchor, 1);
10022 rb_define_method(rb_cMolecule, "pi_anchor", s_Molecule_PiAnchorAtIndex, 1);
10023 rb_define_method(rb_cMolecule, "pi_anchor_r", s_Molecule_PiAnchorR, 1);
10024 rb_define_method(rb_cMolecule, "pi_anchor_fract_r", s_Molecule_PiAnchorFractR, 1);
10025 rb_define_method(rb_cMolecule, "count_pi_anchors", s_Molecule_CountPiAnchors, 0);
10026 rb_define_method(rb_cMolecule, "create_pi_anchor_construct", s_Molecule_CreatePiAnchorConstruct, -1);
10027 rb_define_method(rb_cMolecule, "remove_pi_anchor_constructs", s_Molecule_RemovePiAnchorConstructs, 1);
10028 rb_define_alias(rb_cMolecule, "remove_pi_anchor_construct", "remove_pi_anchor_constructs");
10029 rb_define_method(rb_cMolecule, "count_pi_anchor_constructs", s_Molecule_CountPiAnchorConstructs, 0);
10030 rb_define_method(rb_cMolecule, "pi_anchor_construct", s_Molecule_PiAnchorConstructAtIndex, 1);
10032 rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
10033 rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
10034 rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
10035 rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
10036 rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
10038 /* class MolEnumerable */
10039 rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
10040 rb_include_module(rb_cMolEnumerable, rb_mEnumerable);
10041 rb_define_method(rb_cMolEnumerable, "[]", s_MolEnumerable_Aref, 1);
10042 rb_define_method(rb_cMolEnumerable, "length", s_MolEnumerable_Length, 0);
10043 rb_define_alias(rb_cMolEnumerable, "size", "length");
10044 rb_define_method(rb_cMolEnumerable, "each", s_MolEnumerable_Each, 0);
10046 /* class AtomRef */
10047 rb_cAtomRef = rb_define_class_under(rb_mMolby, "AtomRef", rb_cObject);
10048 for (i = 0; s_AtomAttrDefTable[i].name != NULL; i++) {
10050 snprintf(buf, sizeof buf - 1, "%s", s_AtomAttrDefTable[i].name);
10051 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].getter, 0);
10052 s_AtomAttrDefTable[i].id = rb_intern(buf);
10053 *(s_AtomAttrDefTable[i].symref) = ID2SYM(s_AtomAttrDefTable[i].id);
10055 rb_define_method(rb_cAtomRef, buf, s_AtomAttrDefTable[i].setter, 1);
10057 rb_define_method(rb_cAtomRef, "[]=", s_AtomRef_SetAttr, 2);
10058 rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
10059 rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
10060 rb_define_alias(rb_cAtomRef, "get_attr", "[]");
10061 s_SetAtomAttrString = rb_str_new2("set_atom_attr");
10062 rb_global_variable(&s_SetAtomAttrString);
10063 rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
10065 /* class Parameter */
10066 rb_cParameter = rb_define_class_under(rb_mMolby, "Parameter", rb_cObject);
10067 rb_define_singleton_method(rb_cParameter, "builtin", s_Parameter_Builtin, 0);
10068 rb_define_method(rb_cParameter, "bond", s_Parameter_Bond, 1);
10069 rb_define_method(rb_cParameter, "angle", s_Parameter_Angle, 1);
10070 rb_define_method(rb_cParameter, "dihedral", s_Parameter_Dihedral, 1);
10071 rb_define_method(rb_cParameter, "improper", s_Parameter_Improper, 1);
10072 rb_define_method(rb_cParameter, "vdw", s_Parameter_Vdw, 1);
10073 rb_define_method(rb_cParameter, "vdw_pair", s_Parameter_VdwPair, 1);
10074 rb_define_method(rb_cParameter, "vdw_cutoff", s_Parameter_VdwCutoff, 1);
10075 rb_define_method(rb_cParameter, "element", s_Parameter_Element, 1);
10076 rb_define_method(rb_cParameter, "nbonds", s_Parameter_Nbonds, 0);
10077 rb_define_method(rb_cParameter, "nangles", s_Parameter_Nangles, 0);
10078 rb_define_method(rb_cParameter, "ndihedrals", s_Parameter_Ndihedrals, 0);
10079 rb_define_method(rb_cParameter, "nimpropers", s_Parameter_Nimpropers, 0);
10080 rb_define_method(rb_cParameter, "nvdws", s_Parameter_Nvdws, 0);
10081 rb_define_method(rb_cParameter, "nvdw_pairs", s_Parameter_NvdwPairs, 0);
10082 rb_define_method(rb_cParameter, "nvdw_cutoffs", s_Parameter_NvdwCutoffs, 0);
10083 rb_define_method(rb_cParameter, "nelements", s_Parameter_Nelements, 0);
10084 rb_define_method(rb_cParameter, "bonds", s_Parameter_Bonds, 0);
10085 rb_define_method(rb_cParameter, "angles", s_Parameter_Angles, 0);
10086 rb_define_method(rb_cParameter, "dihedrals", s_Parameter_Dihedrals, 0);
10087 rb_define_method(rb_cParameter, "impropers", s_Parameter_Impropers, 0);
10088 rb_define_method(rb_cParameter, "vdws", s_Parameter_Vdws, 0);
10089 rb_define_method(rb_cParameter, "vdw_pairs", s_Parameter_VdwPairs, 0);
10090 rb_define_method(rb_cParameter, "vdw_cutoffs", s_Parameter_VdwCutoffs, 0);
10091 rb_define_method(rb_cParameter, "elements", s_Parameter_Elements, 0);
10092 rb_define_method(rb_cParameter, "lookup", s_Parameter_LookUp, -1);
10093 rb_define_const(rb_cParameter, "Builtin", Data_Wrap_Struct(rb_cParameter, 0, NULL, NULL));
10095 /* class ParEnumerable */
10096 rb_cParEnumerable = rb_define_class_under(rb_mMolby, "ParEnumerable", rb_cObject);
10097 rb_include_module(rb_cParEnumerable, rb_mEnumerable);
10098 rb_define_method(rb_cParEnumerable, "par_type", s_ParEnumerable_ParType, 0);
10099 rb_define_method(rb_cParEnumerable, "[]", s_ParEnumerable_Aref, 1);
10100 rb_define_method(rb_cParEnumerable, "length", s_ParEnumerable_Length, 0);
10101 rb_define_method(rb_cParEnumerable, "each", s_ParEnumerable_Each, 0);
10102 rb_define_method(rb_cParEnumerable, "reverse_each", s_ParEnumerable_ReverseEach, 0);
10103 rb_define_method(rb_cParEnumerable, "insert", s_ParEnumerable_Insert, -1);
10104 rb_define_method(rb_cParEnumerable, "delete", s_ParEnumerable_Delete, 1);
10105 rb_define_method(rb_cParEnumerable, "lookup", s_ParEnumerable_LookUp, -1);
10107 /* class ParameterRef */
10108 rb_cParameterRef = rb_define_class_under(rb_mMolby, "ParameterRef", rb_cObject);
10109 for (i = 0; s_ParameterAttrDefTable[i].name != NULL; i++) {
10111 snprintf(buf, sizeof buf - 1, "%s", s_ParameterAttrDefTable[i].name);
10112 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].getter, 0);
10113 s_ParameterAttrDefTable[i].id = rb_intern(buf);
10114 if (s_ParameterAttrDefTable[i].symref != NULL)
10115 *(s_ParameterAttrDefTable[i].symref) = ID2SYM(s_ParameterAttrDefTable[i].id);
10116 if (s_ParameterAttrDefTable[i].setter != NULL) {
10118 rb_define_method(rb_cParameterRef, buf, s_ParameterAttrDefTable[i].setter, 1);
10121 rb_define_method(rb_cParameterRef, "[]=", s_ParameterRef_SetAttr, 2);
10122 rb_define_alias(rb_cParameterRef, "set_attr", "[]=");
10123 rb_define_method(rb_cParameterRef, "[]", s_ParameterRef_GetAttr, 1);
10124 rb_define_alias(rb_cParameterRef, "get_attr", "[]");
10125 rb_define_method(rb_cParameterRef, "to_hash", s_ParameterRef_ToHash, 0);
10126 rb_define_method(rb_cParameterRef, "to_s", s_ParameterRef_ToString, 0);
10127 rb_define_method(rb_cParameterRef, "keys", s_ParameterRef_Keys, 0);
10129 /* class MolbyError */
10130 rb_eMolbyError = rb_define_class_under(rb_mMolby, "MolbyError", rb_eStandardError);
10132 /* module Kernel */
10133 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
10134 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
10135 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
10136 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
10137 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
10138 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
10139 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
10140 rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
10141 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
10142 rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
10143 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
10144 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
10145 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
10146 rb_define_method(rb_mKernel, "document_home", s_Kernel_DocumentHome, 0);
10147 rb_define_method(rb_mKernel, "call_subprocess", s_Kernel_CallSubProcess, 2);
10148 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
10149 rb_define_method(rb_mKernel, "error_message_box", s_Kernel_ErrorMessageBox, 1);
10150 rb_define_method(rb_mKernel, "enable_filter_mode", s_Kernel_EnableFilterMode, 0);
10152 s_ID_equal = rb_intern("==");
10155 #pragma mark ====== External functions ======
10157 static VALUE s_ruby_top_self = Qfalse;
10160 s_evalRubyScriptOnMoleculeSub(VALUE val)
10162 void **ptr = (void **)val;
10163 Molecule *mol = (Molecule *)ptr[1];
10164 VALUE sval = rb_str_new2((char *)ptr[0]);
10166 if (s_ruby_top_self == Qfalse) {
10167 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
10169 if (ptr[2] == NULL) {
10172 fnval = Ruby_NewFileStringValue((char *)ptr[2]);
10176 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
10178 return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
10180 VALUE mval = ValueFromMolecule(mol);
10182 return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
10183 else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
10188 Molby_evalRubyScriptOnMolecule(const char *script, Molecule *mol, const char *fname, int *status)
10192 VALUE save_interrupt_flag;
10193 char *save_ruby_sourcefile;
10194 int save_ruby_sourceline;
10195 if (gMolbyIsCheckingInterrupt) {
10196 MolActionAlertRubyIsRunning();
10198 return (RubyValue)Qnil;
10201 args[0] = (void *)script;
10202 args[1] = (void *)mol;
10203 args[2] = (void *)fname;
10204 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
10205 save_ruby_sourcefile = ruby_sourcefile;
10206 save_ruby_sourceline = ruby_sourceline;
10207 retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
10208 s_SetInterruptFlag(Qnil, save_interrupt_flag);
10209 ruby_sourcefile = save_ruby_sourcefile;
10210 ruby_sourceline = save_ruby_sourceline;
10216 Molby_showRubyValue(RubyValue value)
10218 VALUE val = (VALUE)value;
10219 if (gMolbyIsCheckingInterrupt) {
10220 MolActionAlertRubyIsRunning();
10226 val = rb_protect(rb_inspect, val, &status);
10228 MyAppCallback_showScriptMessage("%s", StringValuePtr(val));
10233 Molby_showError(int status)
10235 static const int tag_raise = 6;
10236 char *msg = NULL, *msg2;
10237 VALUE val, backtrace;
10238 int interrupted = 0;
10239 if (status == tag_raise) {
10240 VALUE eclass = CLASS_OF(ruby_errinfo);
10241 if (eclass == rb_eInterrupt) {
10247 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
10249 val = rb_eval_string_protect("$!.to_s", &status);
10251 msg = RSTRING_PTR(val);
10252 else msg = "(message not available)";
10254 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
10255 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
10261 Molby_startup(const char *script, const char *dir)
10268 /* Initialize Ruby interpreter */
10271 /* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
10273 asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
10274 ruby_incpush(libpath);
10278 ruby_script("Molby");
10280 /* Find the resource path (the parent directory of the given directory) */
10281 respath = strdup(dir);
10282 p = strrchr(respath, '/');
10283 if (p == NULL && PATH_SEPARATOR != '/')
10284 p = strrchr(respath, PATH_SEPARATOR);
10287 val = Ruby_NewFileStringValue(respath);
10288 rb_define_global_const("MolbyResourcePath", val);
10291 /* Define Molby classes */
10293 RubyDialogInitClass();
10295 rb_define_const(rb_mMolby, "ResourcePath", val);
10296 val = Ruby_NewFileStringValue(dir);
10297 rb_define_const(rb_mMolby, "ScriptPath", val);
10298 asprintf(&p, "%s%c%s", dir, PATH_SEPARATOR, "mbsf");
10299 val = Ruby_NewFileStringValue(p);
10300 rb_define_const(rb_mMolby, "MbsfPath", val);
10305 /* Create objects for stdout and stderr */
10306 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10307 rb_define_singleton_method(val, "write", s_StandardOutput, 1);
10308 rb_gv_set("$stdout", val);
10309 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10310 rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
10311 rb_gv_set("$stderr", val);
10313 /* Create objects for stdin */
10314 val = rb_funcall(rb_cObject, rb_intern("new"), 0);
10315 rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
10316 rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
10317 rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
10318 rb_gv_set("$stdin", val);
10322 /* Global variable to hold backtrace */
10323 rb_define_variable("$backtrace", &gMolbyBacktrace);
10326 /* Register interrupt check code */
10327 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
10330 /* Get version/copyright string from Ruby interpreter */
10332 gRubyVersion = strdup(ruby_version);
10333 asprintf(&gRubyCopyright, " Copyright (C) %d-%d %s",
10334 RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
10338 /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
10339 s_SetIntervalTimer(0, 50);
10342 /* Read the startup script */
10343 if (script != NULL && script[0] != 0) {
10344 MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
10346 rb_load_protect(rb_str_new2(script), 0, &status);
10349 Molby_showError(status);
10351 MyAppCallback_showScriptMessage("Done.\n");
10356 Molby_buildARGV(int argc, const char **argv)
10359 rb_ary_clear(rb_argv);
10360 for (i = 0; i < argc; i++) {
10361 VALUE arg = rb_tainted_str_new2(argv[i]);
10363 rb_ary_push(rb_argv, arg);