#include <ctype.h>
#include <limits.h>
-#include <version.h> /* for Ruby version */
-#include <node.h> /* for rb_add_event_hook() */
+#include "version.h" /* for Ruby version */
+#include "ruby/version.h" /* for RUBY_BIRTH_YEAR etc. */
+#include "ruby/encoding.h" /* for rb_str_encode() etc. */
+/*#include <node.h> *//* for rb_add_event_hook() */
#if defined(__WXMAC__) || defined(__CMDMAC__)
#define USE_PTHREAD_FOR_TIMER 1
VALUE rb_cParameter, rb_cParEnumerable, rb_cParameterRef;
VALUE gMolbyBacktrace;
+VALUE gMolbyErrorHistory;
+VALUE gScriptMenuCommands;
+VALUE gScriptMenuEnablers;
int gMolbyRunLevel = 0;
int gMolbyIsCheckingInterrupt = 0;
s_FractRSym, s_FractXSym, s_FractYSym, s_FractZSym,
s_SigmaSym, s_SigmaXSym, s_SigmaYSym, s_SigmaZSym,
s_VSym, s_FSym, s_OccupancySym, s_TempFactorSym,
- s_AnisoSym, s_SymopSym, s_IntChargeSym, s_FixForceSym,
- s_FixPosSym, s_ExclusionSym, s_MMExcludeSym, s_PeriodicExcludeSym,
- s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
+ s_AnisoSym, s_AnisoEigvalSym, s_SymopSym, s_IntChargeSym,
+ s_FixForceSym, s_FixPosSym, s_ExclusionSym, s_MMExcludeSym,
+ s_PeriodicExcludeSym, s_HiddenSym, s_AnchorListSym, s_UFFTypeSym;
/* Symbols for parameter attributes */
static VALUE
s_ReqSym, s_EpsSym,
/* s_A14Sym, s_B14Sym, */
s_Req14Sym, s_Eps14Sym,
- s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym,
+ s_CutoffSym, s_RadiusSym, s_ColorSym, s_FullNameSym, s_VdwRadiusSym,
s_CommentSym, s_SourceSym;
+/* Symbols for graphics */
+static VALUE
+ s_LineSym, s_PolySym, s_CylinderSym, s_ConeSym, s_EllipsoidSym;
+
/*
* Utility function
* Get ary[i] by calling "[]" method
#if __WXMSW__
char *p = strdup(StringValuePtr(*valp));
translate_char(p, '/', '\\');
- *valp = rb_str_new2(p);
+ *valp = Ruby_NewEncodedStringValue2(p);
free(p);
return StringValuePtr(*valp);
#else
VALUE retval;
char *p = strdup(fstr);
translate_char(p, '\\', '/');
- retval = rb_str_new2(p);
+ retval = Ruby_NewEncodedStringValue2(p);
free(p);
return retval;
#else
- return rb_str_new2(fstr);
+ return Ruby_NewEncodedStringValue2(fstr);
#endif
}
+char *
+Ruby_EncodedStringValuePtr(VALUE *valp)
+{
+ rb_string_value(valp);
+ *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
+ return RSTRING_PTR(*valp);
+}
+
+VALUE
+Ruby_NewEncodedStringValue(const char *str, int len)
+{
+ if (len <= 0)
+ len = strlen(str);
+ return rb_enc_str_new(str, len, rb_default_external_encoding());
+}
+
+VALUE
+Ruby_NewEncodedStringValue2(const char *str)
+{
+ return Ruby_NewEncodedStringValue(str, -1);
+}
+
VALUE
Ruby_ObjToStringObj(VALUE val)
{
} else buf[0] = 0;
retval = MyAppCallback_getTextWithPrompt(StringValuePtr(prompt), buf, sizeof buf);
if (retval)
- return rb_str_new2(buf);
+ return Ruby_NewEncodedStringValue2(buf);
else
return Qnil;
}
/*
* call-seq:
+ * export_to_clipboard(str)
+ *
+ * Export the given string to clipboard.
+ */
+static VALUE
+s_Kernel_ExportToClipboard(VALUE self, VALUE sval)
+{
+#if !defined(__CMDMAC__)
+ const char *s;
+ char *ns;
+ if (!gUseGUI)
+ return Qnil;
+ s = StringValuePtr(sval);
+#if __WXMSW__
+ /* Convert the end-of-line characters */
+ { const char *p; int nc; char *np;
+ nc = 0;
+ for (p = s; *p != 0; p++) {
+ if (*p == '\n')
+ nc++;
+ }
+ ns = (char *)malloc(strlen(s) + nc + 1);
+ for (np = ns, p = s; *p != 0; p++, np++) {
+ if (*p == '\n')
+ *np++ = '\r';
+ *np = *p;
+ }
+ *np = 0;
+ }
+#else
+ ns = (char *)malloc(strlen(s) + 1);
+ strcpy(ns, s);
+#if __WXMAC__
+ { char *np;
+ /* wxMac still has Carbon code. Oops. */
+ for (np = ns; *np != 0; np++) {
+ if (*np == '\n')
+ *np = '\r';
+ }
+ }
+#endif
+#endif
+ if (MoleculeCallback_writeToPasteboard("TEXT", ns, strlen(ns) + 1))
+ rb_raise(rb_eMolbyError, "Cannot export string to clipboard");
+#endif
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * hartree_to_kcal(val)
+ *
+ * Convert hartree to kcal/mol
+ */
+static VALUE
+s_Kernel_HartreeToKcal(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d * 627.5094740630557);
+}
+
+/*
+ * call-seq:
+ * kcal_to_hartree(val)
+ *
+ * Convert kcal/mol to hartree
+ */
+static VALUE
+s_Kernel_KcalToHartree(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d / 627.5094740630557);
+}
+
+/*
+ * call-seq:
+ * hartree_to_kj(val)
+ *
+ * Convert hartree to kJ/mol
+ */
+static VALUE
+s_Kernel_HartreeToKJ(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d * 2625.4996394798253);
+}
+
+/*
+ * call-seq:
+ * kj_to_hartree(val)
+ *
+ * Convert kJ/mol to hartree
+ */
+static VALUE
+s_Kernel_KJToHartree(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d / 2625.4996394798253);
+}
+
+/*
+ * call-seq:
+ * bohr_to_angstrom(val)
+ *
+ * Convert bohr to angstrom
+ */
+static VALUE
+s_Kernel_BohrToAngstrom(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d * 0.529177210903);
+}
+
+/*
+ * call-seq:
+ * angstrom_to_bohr(val)
+ *
+ * Convert angstrom to bohr
+ */
+static VALUE
+s_Kernel_AngstromToBohr(VALUE self, VALUE fval)
+{
+ double d = NUM2DBL(rb_Float(fval));
+ return rb_float_new(d / 0.529177210903);
+}
+
+/*
+ * call-seq:
* stdout.write(str)
*
* Put the message in the main text view in black color.
/*
* call-seq:
+ * stdout.flush
+ * stderr.flush
+ *
+ * Flush the standard (error) output. Actually do nothing.
+ */
+static VALUE
+s_FlushConsoleOutput(VALUE self)
+{
+ return self;
+}
+
+/*
+ * call-seq:
* stdin.gets(rs = $/)
*
* Read one line message via dialog box.
s_StandardInputGets(int argc, VALUE *argv, VALUE self)
{
VALUE pval, rval;
- pval = rb_str_new2("Enter a line:");
+ pval = Ruby_NewEncodedStringValue2("Enter a line:");
rval = s_Kernel_Ask(1, &pval, self);
if (rval == Qnil)
rb_interrupt();
oldval = s_interrupt_flag;
if (val != Qundef) {
s_interrupt_flag = val;
- if (val == Qfalse) {
- s_HideProgressPanel(self);
- }
}
return oldval;
}
return s_SetInterruptFlag(self, Qundef);
}
-#if 0
-static VALUE
-s_Ruby_CallMethod(VALUE val)
-{
- void **ptr = (void **)val;
- VALUE receiver = (VALUE)ptr[0];
- ID method_id = (ID)ptr[1];
- VALUE args = (VALUE)ptr[2];
- VALUE retval;
- if (method_id == 0) {
- /* args should be a string, which is evaluated */
- if (receiver == Qnil) {
- retval = rb_eval_string(StringValuePtr(args));
- } else {
- retval = rb_obj_instance_eval(1, &args, receiver);
- }
- } else {
- /* args should be an array of arguments */
- retval = rb_apply(receiver, method_id, args);
- }
- return retval;
-}
-
-VALUE
-Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
-{
- VALUE retval, save_interrupt_flag;
- void *ptr[3];
- save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
- ptr[0] = (void *)receiver;
- ptr[1] = (void *)method_id;
- ptr[2] = (void *)args;
- MyAppCallback_beginUndoGrouping();
- retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
- MyAppCallback_endUndoGrouping();
- s_SetInterruptFlag(Qnil, save_interrupt_flag);
- MyAppCallback_hideProgressPanel(); /* In case when the progress panel is still onscreen */\v
- return retval;
-}
-#endif
-
VALUE
Ruby_SetInterruptFlag(VALUE val)
{
}
static void
-s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
+//s_Event_Callback(rb_event_t event, NODE *node, VALUE self, ID rid, VALUE klass)
+s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
{
if (s_interrupt_flag != Qfalse) {
static unsigned long sLastTime = 0;
/*
* call-seq:
- * register_menu(title, method)
+ * register_menu(title, method, enable_proc = nil)
*
* Register the method (specified as a symbol) in the script menu.
* The method must be either an instance method of Molecule with no argument,
- * or a class method of Molecule with one argument (the current molecule).
+ * or a class method of Molecule with one argument (the current molecule),
+ * or a proc object with one argument (the current molecule).
* The menu associated with the class method can be invoked even when no document
* is open (the argument is set to Qnil in this case). On the other hand, the
* menu associated with the instance method can only be invoked when at least one
* document is active.
- */
-static VALUE
-s_Kernel_RegisterMenu(VALUE self, VALUE title, VALUE method)
-{
- if (TYPE(method) == T_SYMBOL) {
- method = rb_funcall(method, rb_intern("to_s"), 0);
+ * If enable_proc is non-nil, then it is called whenever the availability of
+ * the menu command is tested. It is usually a proc object with one argument
+ * (the current molecule or nil). As a special case, the following symbols can
+ * be given; :mol (enabled when any molecule is open), :non_empty (enabled when
+ * the top-level molecule has at least one atom), :selection (enabled when
+ * the top-level molecule has one or more selected atoms).
+ */
+static VALUE
+s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
+{
+ int n, mtype = 0;
+ VALUE tval, mval, pval;
+ static VALUE sMolSym, sNonEmptySym, sSelectionSym;
+ static VALUE sMolProc, sNonEmptyProc, sSelectionProc, sTrueProc;
+ rb_scan_args(argc, argv, "21", &tval, &mval, &pval);
+ tval = rb_str_to_str(tval);
+ n = MyAppCallback_registerScriptMenu(StringValuePtr(tval));
+ if (n < 0)
+ return Qnil;
+ if (TYPE(mval) == T_SYMBOL) {
+ /* Create an appropriate proc object */
+ const char *name = rb_id2name(SYM2ID(mval));
+ char *s;
+ if (rb_funcall(rb_cMolecule, rb_intern("method_defined?"), 1, mval) != Qfalse) {
+ /* Defined as a Molecule method */
+ asprintf(&s, "lambda { |m| m.%s }", name);
+ mtype = 1;
+ } else if (rb_respond_to(rb_cMolecule, SYM2ID(mval))) {
+ /* Defined as a Molecule class method */
+ asprintf(&s, "lambda { |m| Molecule.%s(m) }", name);
+ mtype = 2;
+ } else rb_raise(rb_eMolbyError, "The method %s is not defined in Molecule", name);
+ mval = rb_eval_string(s);
+ free(s);
+ }
+ if (sMolSym == Qfalse) {
+ sMolSym = ID2SYM(rb_intern("mol"));
+ sNonEmptySym = ID2SYM(rb_intern("non_empty"));
+ sSelectionSym = ID2SYM(rb_intern("selection"));
+ sMolProc = rb_eval_string("lambda { |m| m != nil }");
+ rb_define_variable("$is_a_molecule_proc", &sMolProc);
+ sNonEmptyProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.natoms > 0 }");
+ rb_define_variable("$is_molecule_not_empty_proc", &sNonEmptyProc);
+ sSelectionProc = rb_eval_string("lambda { |m| m.is_a?(Molecule) && m.selection.count > 0 }");
+ rb_define_variable("$has_molecule_selection_proc", &sSelectionProc);
+ sTrueProc = rb_eval_string("lambda { |m| true }");
+ rb_define_variable("$always_true_proc", &sTrueProc);
}
- MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title));
- return self;
+
+ if (pval == Qnil) {
+ if (mtype == 1)
+ pval = sMolProc;
+ else
+ pval = sTrueProc;
+ } else if (pval == sMolSym)
+ pval = sMolProc;
+ else if (pval == sNonEmptySym)
+ pval = sNonEmptyProc;
+ else if (pval == sSelectionSym)
+ pval = sSelectionProc;
+ rb_ary_store(gScriptMenuCommands, n, mval);
+ rb_ary_store(gScriptMenuEnablers, n, pval);
+ return INT2NUM(n);
}
static VALUE
}
static VALUE
+s_Ruby_UpdateUI_handler(VALUE data)
+{
+ void **p = (void **)data;
+ int index = (intptr_t)p[0];
+ Molecule *mol = (Molecule *)p[1];
+ int *outChecked = (int *)p[2];
+ char **outTitle = (char **)p[3];
+ VALUE mval = ValueFromMolecule(mol);
+ VALUE pval = rb_ary_entry(gScriptMenuEnablers, index);
+ static ID call_id = 0;
+ if (call_id == 0)
+ call_id = rb_intern("call");
+ if (pval == Qnil)
+ return Qnil;
+ pval = rb_funcall(pval, call_id, 1, mval);
+ if (rb_obj_is_kind_of(pval, rb_cArray)) {
+ VALUE val;
+ if (outChecked != NULL) {
+ val = rb_ary_entry(pval, 1); /* Checked or not */
+ *outChecked = (RTEST(val) ? 1 : 0);
+ }
+ if (outTitle != NULL) {
+ val = rb_ary_entry(pval, 2); /* Text */
+ if (TYPE(val) == T_STRING) {
+ *outTitle = strdup(StringValuePtr(val));
+ }
+ }
+ pval = rb_ary_entry(pval, 0);
+ }
+ return pval;
+}
+
+int
+Ruby_UpdateUI(int index, Molecule *mol, int *outChecked, char **outTitle)
+{
+ int status;
+ void *p[4];
+ VALUE retval;
+ p[0] = (void *)(intptr_t)index;
+ p[1] = mol;
+ p[2] = outChecked;
+ p[3] = outTitle;
+ retval = rb_protect(s_Ruby_UpdateUI_handler, (VALUE)p, &status);
+ return (RTEST(retval) ? 1 : 0);
+}
+
+/*
+static VALUE
s_Ruby_methodType_sub(VALUE data)
{
const char **p = (const char **)data;
else ival = 0;
return INT2FIX(ival);
}
-
+*/
/* Returns 1 if the class defines the instance method with the given name, 2 if the class
has the singleton method (class method) with the given name, 0 otherwise. */
-int
+/*int
Ruby_methodType(const char *className, const char *methodName)
{
int status;
return FIX2INT(retval);
else return 0;
}
+*/
/*
* call-seq:
s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
{
VALUE cmd, procname, cproc, stdout_val, stderr_val;
- int n;
+ VALUE save_interruptFlag;
+ int n, exitstatus, pid;
char *sout, *serr;
+ const char *pnamestr;
FILE *fpout, *fperr;
rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
rb_raise(rb_eMolbyError, "Cannot open file for standard output (%s)", serr);
}
}
-
- n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr);
-
+
+ save_interruptFlag = s_SetInterruptFlag(self, Qnil);
+ if (procname != Qnil)
+ pnamestr = StringValuePtr(procname);
+ else pnamestr = NULL;
+ n = MyAppCallback_callSubProcess(StringValuePtr(cmd), pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
+ s_SetInterruptFlag(self, save_interruptFlag);
+
if (fpout != NULL && fpout != (FILE *)1)
fclose(fpout);
if (fperr != NULL && fperr != (FILE *)1)
s_Kernel_Backquote(VALUE self, VALUE cmd)
{
char *buf;
- int n;
+ int n, exitstatus, pid;
VALUE val;
- n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL);
- if (n != 0)
- rb_raise(rb_eMolbyError, "Cannot invoke command '%s'", StringValuePtr(cmd));
- if (buf != NULL) {
- val = rb_str_new2(buf);
+ n = MyAppCallback_callSubProcess(StringValuePtr(cmd), NULL, DUMMY_CALLBACK, &buf, NULL, NULL, &exitstatus, &pid);
+/* fprintf(stderr, "n = %d, exitstatus = %d, pid = %d\n", n, exitstatus, pid); */
+ if (n >= 0 && buf != NULL) {
+ val = Ruby_NewEncodedStringValue(buf, 0);
free(buf);
} else {
- val = rb_str_new2("");
+ val = Ruby_NewEncodedStringValue("", 0);
}
+ rb_last_status_set(exitstatus, pid);
return val;
}
return value;
}
+#pragma mark ====== IO extension ======
+
+static VALUE
+s_Ruby_str_encode_protected(VALUE val)
+{
+ return rb_str_encode(val, rb_enc_from_encoding(rb_default_external_encoding()),
+ ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
+}
+
+/*
+ * call-seq:
+ * gets_any_eol
+ *
+ * A gets variant that works for CR, LF, and CRLF end-of-line characters.
+ */
+static VALUE
+s_IO_gets_any_eol(VALUE self)
+{
+ VALUE val, val2, cval;
+ char buf[1024];
+ int i, c, status;
+ static ID id_getbyte = 0, id_ungetbyte;
+ if (id_getbyte == 0) {
+ id_getbyte = rb_intern("getbyte");
+ id_ungetbyte = rb_intern("ungetbyte");
+ }
+ i = 0;
+ val = Qnil;
+ while ((cval = rb_funcall(self, id_getbyte, 0)) != Qnil) {
+ c = NUM2INT(rb_Integer(cval));
+ if (c == 0x0d) {
+ cval = rb_funcall(self, id_getbyte, 0);
+ if (cval != Qnil) {
+ c = NUM2INT(rb_Integer(cval));
+ if (c != 0x0a)
+ rb_funcall(self, id_ungetbyte, 1, cval);
+ }
+ break;
+ } else if (c != 0x0a) {
+ buf[i++] = c;
+ if (i >= 1020) {
+ buf[i] = 0;
+ if (val == Qnil)
+ val = rb_str_new(buf, i);
+ else
+ rb_str_append(val, rb_str_new(buf, i));
+ i = 0;
+ }
+ } else break;
+ }
+ if (cval == Qnil && i == 0 && val == Qnil)
+ return Qnil; /* End of file */
+ buf[i] = 0;
+ if (val == Qnil)
+ val = rb_str_new(buf, i);
+ else if (i > 0)
+ rb_str_append(val, rb_str_new(buf, i));
+ val2 = rb_protect(s_Ruby_str_encode_protected, val, &status); /* Ignore exception */
+ if (status == 0)
+ val = val2;
+ if (cval != Qnil) {
+ /* Needs a end-of-line mark */
+ cval = rb_gv_get("$/");
+ rb_str_append(val, cval);
+ }
+ rb_gv_set("$_", val);
+ return val;
+}
+
#pragma mark ====== Utility functions (protected funcall) ======
struct Ruby_funcall2_record {
Int tp;
s_UnionParFromValue(self, &tp, 0);
if (tp == kElementParType)
- return rb_str_new2("element");
+ return Ruby_NewEncodedStringValue2("element");
tp -= kFirstParType;
if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
- return rb_str_new2(s_ParameterTypeNames[tp]);
+ return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
}
* call-seq:
* radius -> Float
*
- * Get the atomic radius for the atom display parameter.
+ * Get the atomic (covalent) radius for the element parameter.
*/
static VALUE s_ParameterRef_GetRadius(VALUE self) {
UnionPar *up;
/*
* call-seq:
+ * vdw_radius -> Float
+ *
+ * Get the van der Waals radius for the element parameter. (0 if not given)
+ */
+static VALUE s_ParameterRef_GetVdwRadius(VALUE self) {
+ UnionPar *up;
+ Int tp;
+ up = s_UnionParFromValue(self, &tp, 0);
+ if (tp == kElementParType)
+ return rb_float_new(up->atom.vdw_radius);
+ else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
+}
+
+/*
+ * call-seq:
* color -> [Float, Float, Float]
*
- * Get the rgb color for the atom display parameter.
+ * Get the rgb color for the element parameter.
*/
static VALUE s_ParameterRef_GetColor(VALUE self) {
UnionPar *up;
Int tp;
up = s_UnionParFromValue(self, &tp, 0);
if (tp == kElementParType)
- return rb_ary_new3(3, rb_float_new(up->atom.r), rb_float_new(up->atom.g), rb_float_new(up->atom.b));
+ return rb_ary_new3(3, rb_float_new(up->atom.red / 65535.0), rb_float_new(up->atom.green / 65535.0), rb_float_new(up->atom.blue / 65535.0));
else rb_raise(rb_eMolbyError, "invalid member color");
}
* call-seq:
* atomic_number -> Integer
*
- * Get the atomic number for the vdw or atom parameter.
+ * Get the atomic number for the vdw or element parameter.
*/
static VALUE s_ParameterRef_GetAtomicNumber(VALUE self) {
UnionPar *up;
* call-seq:
* name -> String
*
- * Get the name for the atom display parameter.
+ * Get the name for the element parameter.
*/
static VALUE s_ParameterRef_GetName(VALUE self) {
UnionPar *up;
char name[5];
strncpy(name, up->atom.name, 4);
name[4] = 0;
- return rb_str_new2(name);
+ return Ruby_NewEncodedStringValue2(name);
} else rb_raise(rb_eMolbyError, "invalid member name");
}
* call-seq:
* weight -> Float
*
- * Get the atomic weight for the atom display parameter.
+ * Get the atomic weight for the element parameter.
*/
static VALUE s_ParameterRef_GetWeight(VALUE self) {
UnionPar *up;
* call-seq:
* fullname -> String
*
- * Get the full name for the atom display parameter.
+ * Get the full name for the element parameter.
*/
static VALUE s_ParameterRef_GetFullName(VALUE self) {
UnionPar *up;
char fullname[16];
strncpy(fullname, up->atom.fullname, 15);
fullname[15] = 0;
- return rb_str_new2(fullname);
+ return Ruby_NewEncodedStringValue2(fullname);
} else rb_raise(rb_eMolbyError, "invalid member fullname");
}
com = up->bond.com;
if (com == 0)
return Qnil;
- else return rb_str_new2(ParameterGetComment(com));
+ else return Ruby_NewEncodedStringValue2(ParameterGetComment(com));
}
/*
return Qfalse; /* undefined */
else if (src == 0)
return Qnil; /* local */
- else return rb_str_new2(ParameterGetComment(src));
+ else return Ruby_NewEncodedStringValue2(ParameterGetComment(src));
}
static void
return val;
}
+static VALUE s_ParameterRef_SetVdwRadius(VALUE self, VALUE val) {
+ UnionPar *up;
+ Int tp, oldsrc;
+ VALUE oldval;
+ up = s_UnionParFromValue(self, &tp, 1);
+ oldval = s_ParameterRef_GetVdwRadius(self);
+ oldsrc = up->bond.src;
+ val = rb_Float(val);
+ if (tp == kElementParType) {
+ up->atom.vdw_radius = NUM2DBL(val);
+ } else rb_raise(rb_eMolbyError, "invalid member vdw_radius");
+ s_RegisterUndoForParameterAttrChange(self, s_VdwRadiusSym, val, oldval, oldsrc);
+ return val;
+}
+
static VALUE s_ParameterRef_SetColor(VALUE self, VALUE val) {
UnionPar *up;
Int tp, oldsrc;
rb_raise(rb_eMolbyError, "value should be an array of three floats (r, g, b)");
valp = RARRAY_PTR(val);
if (tp == kElementParType) {
- up->atom.r = NUM2DBL(rb_Float(valp[0]));
- up->atom.g = NUM2DBL(rb_Float(valp[1]));
- up->atom.b = NUM2DBL(rb_Float(valp[2]));
+ up->atom.red = (unsigned short)(NUM2DBL(rb_Float(valp[0])) * 65535.0);
+ up->atom.green = (unsigned short)(NUM2DBL(rb_Float(valp[1])) * 65535.0);
+ up->atom.blue = (unsigned short)(NUM2DBL(rb_Float(valp[2])) * 65535.0);
} else rb_raise(rb_eMolbyError, "invalid member color");
s_RegisterUndoForParameterAttrChange(self, s_ColorSym, val, oldval, oldsrc);
return val;
{"eps14", &s_Eps14Sym, 0, s_ParameterRef_GetEps14, s_ParameterRef_SetEps14},
{"cutoff", &s_CutoffSym, 0, s_ParameterRef_GetCutoff, s_ParameterRef_SetCutoff},
{"radius", &s_RadiusSym, 0, s_ParameterRef_GetRadius, s_ParameterRef_SetRadius},
+ {"vdw_radius", &s_VdwRadiusSym, 0, s_ParameterRef_GetVdwRadius, s_ParameterRef_SetVdwRadius},
{"color", &s_ColorSym, 0, s_ParameterRef_GetColor, s_ParameterRef_SetColor},
{"atomic_number",&s_AtomicNumberSym, 0, s_ParameterRef_GetAtomicNumber, s_ParameterRef_SetAtomicNumber},
{"name", &s_NameSym, 0, s_ParameterRef_GetName, s_ParameterRef_SetName},
case kVdwCutoffParType:
return rb_ary_new3(6, s_IndexSym, s_ParTypeSym, s_AtomTypesSym, s_CutoffSym, s_CommentSym, s_SourceSym);
case kElementParType:
- 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);
+ return rb_ary_new3(10, s_IndexSym, s_ParTypeSym, s_AtomicNumberSym, s_NameSym, s_FullNameSym, s_RadiusSym, s_ColorSym, s_WeightSym, s_VdwRadiusSym, s_CommentSym, s_SourceSym);
default:
rb_raise(rb_eMolbyError, "internal error: invalid parameter type");
}
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);
break;
case kElementParType:
- 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);
+ snprintf(buf, sizeof buf, "element %2.2s %3d %6.3f %6.3f %6.3f %6.3f %8.4f %s %6.3f", up->atom.name, up->atom.number, up->atom.radius, up->atom.red / 65535.0, up->atom.green / 65535.0, up->atom.blue / 65535.0, up->atom.weight, up->atom.fullname, up->atom.vdw_radius);
break;
}
- return rb_str_new2(buf);
+ return Ruby_NewEncodedStringValue2(buf);
}
/*
Data_Get_Struct(self, ParEnumerable, pen);
tp = pen->parType;
if (tp == kElementParType)
- return rb_str_new2("element");
+ return Ruby_NewEncodedStringValue2("element");
tp -= kFirstParType;
if (tp >= 0 && tp < sizeof(s_ParameterTypeNames) / sizeof(s_ParameterTypeNames[0]))
- return rb_str_new2(s_ParameterTypeNames[tp]);
+ return Ruby_NewEncodedStringValue2(s_ParameterTypeNames[tp]);
else rb_raise(rb_eMolbyError, "Internal error: parameter type tag is out of range (%d)", tp);
}
static VALUE s_AtomRef_GetSegName(VALUE self) {
char *p = s_AtomFromValue(self)->segName;
- return rb_str_new(p, strlen_limit(p, 4));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
}
static VALUE s_AtomRef_GetResSeq(VALUE self) {
static VALUE s_AtomRef_GetResName(VALUE self) {
char *p = s_AtomFromValue(self)->resName;
- return rb_str_new(p, strlen_limit(p, 4));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
}
static VALUE s_AtomRef_GetName(VALUE self) {
char *p = s_AtomFromValue(self)->aname;
- return rb_str_new(p, strlen_limit(p, 4));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
}
static VALUE s_AtomRef_GetAtomType(VALUE self) {
int type = s_AtomFromValue(self)->type;
char *p = (type == 0 ? "" : AtomTypeDecodeToString(type, NULL));
- return rb_str_new(p, strlen_limit(p, 6));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 6));
}
static VALUE s_AtomRef_GetCharge(VALUE self) {
static VALUE s_AtomRef_GetElement(VALUE self) {
char *p = s_AtomFromValue(self)->element;
- return rb_str_new(p, strlen_limit(p, 4));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
}
static VALUE s_AtomRef_GetAtomicNumber(VALUE self) {
return retval;
}
+static VALUE s_AtomRef_GetAnisoEigenValues(VALUE self) {
+ VALUE retval;
+ int i;
+ Atom *ap = s_AtomFromValue(self);
+ if (ap->aniso == NULL)
+ return Qnil;
+ retval = rb_ary_new();
+ for (i = 0; i < 3; i++)
+ rb_ary_push(retval, rb_float_new(ap->aniso->eigval[i]));
+ return retval;
+}
+
static VALUE s_AtomRef_GetSymop(VALUE self) {
VALUE retval;
Atom *ap = s_AtomFromValue(self);
static VALUE s_AtomRef_GetUFFType(VALUE self) {
char *p = s_AtomFromValue(self)->uff_type;
- return rb_str_new(p, strlen_limit(p, 5));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 5));
}
static VALUE s_AtomRef_SetIndex(VALUE self, VALUE val) {
return val;
}
+static VALUE s_AtomRef_SetAnisoEigenValues(VALUE self, VALUE val) {
+ rb_raise(rb_eMolbyError, "Eigenvalues of anisotropic factors are read-only.");
+ return val; /* Not reached */
+}
+
static VALUE s_AtomRef_SetSymop(VALUE self, VALUE val) {
Molecule *mol;
Atom *ap;
{"occupancy", &s_OccupancySym, 0, s_AtomRef_GetOccupancy, s_AtomRef_SetOccupancy},
{"temp_factor", &s_TempFactorSym, 0, s_AtomRef_GetTempFactor, s_AtomRef_SetTempFactor},
{"aniso", &s_AnisoSym, 0, s_AtomRef_GetAniso, s_AtomRef_SetAniso},
+ {"aniso_eigenvalues", &s_AnisoEigvalSym, 0, s_AtomRef_GetAnisoEigenValues, s_AtomRef_SetAnisoEigenValues},
{"symop", &s_SymopSym, 0, s_AtomRef_GetSymop, s_AtomRef_SetSymop},
{"int_charge", &s_IntChargeSym, 0, s_AtomRef_GetIntCharge, s_AtomRef_SetIntCharge},
{"fix_force", &s_FixForceSym, 0, s_AtomRef_GetFixForce, s_AtomRef_SetFixForce},
if (idx2 < 0 || idx2 >= mol->nresidues)
rb_raise(rb_eIndexError, "residue index out of range (%d; should be %d..%d)", idx1, -mol->nresidues, mol->nresidues - 1);
p = mol->residues[idx2];
- return rb_str_new(p, strlen_limit(p, 4));
+ return Ruby_NewEncodedStringValue(p, strlen_limit(p, 4));
}
}
return Qnil;
#pragma mark ====== Molecule Class ======
-/* An malloc'ed string buffer. Retains the error/warning message from the last ***load/***save method. */
+#pragma mark ------ Allocate/Release/Accessor ------
+
+/* An malloc'ed string buffer. Retains the error/warning message from the last ***load / ***save method. */
/* Accessible from Ruby as Molecule#error_message and Molecule#error_message=. */
char *gLoadSaveErrorMessage = NULL;
s_Molecule_AtomGroupFromValue(VALUE self, VALUE val)
{
IntGroup *ig;
+ Molecule *mp1;
+ Data_Get_Struct(self, Molecule, mp1);
val = rb_funcall(self, rb_intern("atom_group"), 1, val);
if (!rb_obj_is_kind_of(val, rb_cIntGroup))
rb_raise(rb_eMolbyError, "IntGroup instance is expected");
Data_Get_Struct(val, IntGroup, ig);
+ IntGroupRemove(ig, mp1->natoms, ATOMS_MAX_NUMBER); /* Limit the group member to existing atoms */
IntGroupRetain(ig);
return ig;
}
-static void
-s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
-{
- if (gLoadSaveErrorMessage != NULL) {
- MyAppCallback_setConsoleColor(1);
- MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
- MyAppCallback_setConsoleColor(0);
- }
- if (status != 0)
- rb_raise(rb_eMolbyError, "%s %s", msg, fname);
-}
-
/*
* call-seq:
* dup -> Molecule
/*
* call-seq:
+ * atom_index(val) -> Integer
+ *
+ * Returns the atom index represented by val. val can be either a non-negative integer
+ * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
+ * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
+ * where resname, resid, name are the residue name, residue id, and atom name respectively.
+ * If val is a string and multiple atoms match the description, the atom with the lowest index
+ * is returned.
+ */
+static VALUE
+s_Molecule_AtomIndex(VALUE self, VALUE val)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
+}
+
+/*
+ * call-seq:
+ * self == Molecule -> boolean
+ *
+ * True if the two arguments point to the same molecule.
+ */
+static VALUE
+s_Molecule_Equal(VALUE self, VALUE val)
+{
+ if (rb_obj_is_kind_of(val, rb_cMolecule)) {
+ Molecule *mol1, *mol2;
+ Data_Get_Struct(self, Molecule, mol1);
+ Data_Get_Struct(val, Molecule, mol2);
+ return (mol1 == mol2 ? Qtrue : Qfalse);
+ } else return Qfalse;
+}
+
+#pragma mark ------ Load/Save ------
+
+static void
+s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
+{
+ if (gLoadSaveErrorMessage != NULL) {
+ MyAppCallback_setConsoleColor(1);
+ MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
+ MyAppCallback_setConsoleColor(0);
+ }
+ if (status != 0)
+ rb_raise(rb_eMolbyError, "%s %s", msg, fname);
+}
+
+/*
+ * call-seq:
* loadmbsf(file) -> bool
*
* Read a structure from a mbsf file.
rb_scan_args(argc, argv, "1", &fname);
fstr = FileStringValuePtr(fname);
retval = MoleculeLoadMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load mbsf", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load mbsf", fstr);
return Qtrue;
}
rb_scan_args(argc, argv, "11", &fname, &pdbname);
fstr = FileStringValuePtr(fname);
retval = MoleculeLoadPsfFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load psf", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load psf", fstr);
pdbstr = NULL;
if (!NIL_P(pdbname)) {
pdbstr = strdup(FileStringValuePtr(pdbname));
retval = MoleculeReadCoordinatesFromPdbFile(mol, pdbstr, &gLoadSaveErrorMessage);
free(pdbstr);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load coordinates from pdb", pdbstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load coordinates from pdb", pdbstr);
}
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeReadCoordinatesFromPdbFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load pdb", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load pdb", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeReadCoordinatesFromDcdFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load dcd", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load dcd", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeLoadTepFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load ORTEP file", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load ORTEP file", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeLoadShelxFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load SHELX res file", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load SHELX res file", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeLoadGaussianFchkFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load Gaussian fchk", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load Gaussian fchk", fstr);
return Qtrue;
}
MyAppCallback_showProgressPanel("Loading GAMESS dat file...");
retval = MoleculeLoadGamessDatFile(mol, fstr, &gLoadSaveErrorMessage);
MyAppCallback_hideProgressPanel();
- s_Molecule_RaiseOnLoadSave(retval, "Failed to load GAMESS dat", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 1, "Failed to load GAMESS dat", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeWriteToMbsfFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to save mbsf", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save mbsf", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeWriteToPsfFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to save psf", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save psf", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeWriteToPdbFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to save pdb", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save pdb", fstr);
return Qtrue;
}
MoleculeClearLoadSaveErrorMessage();
fstr = FileStringValuePtr(fname);
retval = MoleculeWriteToDcdFile(mol, fstr, &gLoadSaveErrorMessage);
- s_Molecule_RaiseOnLoadSave(retval, "Failed to save dcd", fstr);
+ s_Molecule_RaiseOnLoadSave(retval, 0, "Failed to save dcd", fstr);
return Qtrue;
}
-/*
- * call-seq:
- * savetep(file) -> bool
- *
- * Write coordinates as an ORTEP file. Returns true if successful.
- */
-/*
-static VALUE
-s_Molecule_Savetep(VALUE self, VALUE fname)
-{
- char *fstr;
- Molecule *mol;
- char errbuf[128];
- Data_Get_Struct(self, Molecule, mol);
- fstr = FileStringValuePtr(fname);
- if (MoleculeWriteToTepFile(mol, fstr, errbuf, sizeof errbuf) != 0)
- rb_raise(rb_eMolbyError, errbuf);
- return Qtrue;
-}
-*/
-
-/* load([ftype, ] fname, ...) */
+/* load([ftype, ] fname, ...) */
static VALUE
s_Molecule_LoadSave(int argc, VALUE *argv, VALUE self, int loadFlag)
{
failure:
rval = rb_str_to_str(argv[0]);
asprintf(&p, "Failed to %s file %s", (loadFlag ? "load" : "save"), type);
- s_Molecule_RaiseOnLoadSave(1, p, StringValuePtr(rval));
+ s_Molecule_RaiseOnLoadSave(1, loadFlag, p, StringValuePtr(rval));
return Qnil; /* Does not reach here */
success:
/*
* call-seq:
+ * open -> Molecule
+ * open(file) -> Molecule
+ *
+ * Create a new molecule from file as a document. If file is not given, an untitled document is created.
+ */
+static VALUE
+s_Molecule_Open(int argc, VALUE *argv, VALUE self)
+{
+ VALUE fname;
+ const char *p;
+ Molecule *mp;
+ VALUE iflag;
+
+ if (!gUseGUI) {
+ rb_raise(rb_eMolbyError, "Molecule.open is not usable in non-GUI mode. Use Molecule.new instead.");
+ }
+
+ rb_scan_args(argc, argv, "01", &fname);
+ if (NIL_P(fname))
+ p = NULL;
+ else
+ p = FileStringValuePtr(fname);
+ iflag = Ruby_SetInterruptFlag(Qfalse);
+ mp = MoleculeCallback_openNewMolecule(p);
+ Ruby_SetInterruptFlag(iflag);
+ if (mp == NULL) {
+ if (p == NULL)
+ rb_raise(rb_eMolbyError, "Cannot create untitled document");
+ else
+ rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
+ }
+ return ValueFromMolecule(mp);
+}
+
+/*
+ * call-seq:
+ * new -> Molecule
+ * new(file, *args) -> Molecule
+ *
+ * Create a new molecule and call "load" method with the same arguments.
+ */
+static VALUE
+s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
+{
+ if (argc > 0)
+ return s_Molecule_Load(argc, argv, self);
+ else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
+}
+
+/*
+ * call-seq:
+ * error_message -> String
+ *
+ * Get the error_message from the last load/save method. If no error, returns nil.
+ */
+static VALUE
+s_Molecule_ErrorMessage(VALUE klass)
+{
+ if (gLoadSaveErrorMessage == NULL)
+ return Qnil;
+ else return Ruby_NewEncodedStringValue2(gLoadSaveErrorMessage);
+}
+
+/*
+ * call-seq:
+ * set_error_message(String)
+ * Molecule.error_message = String
+ *
+ * Set the error_message for the present load/save method.
+ */
+static VALUE
+s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
+{
+ if (gLoadSaveErrorMessage != NULL) {
+ free(gLoadSaveErrorMessage);
+ gLoadSaveErrorMessage = NULL;
+ }
+ if (sval != Qnil) {
+ sval = rb_str_to_str(sval);
+ gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
+ }
+ return sval;
+}
+
+/*
+ * call-seq:
+ * set_molecule(Molecule)
+ *
+ * Duplicate the given molecule and set to self. The present molecule must be empty.
+ * This method is exclusively used for associating a new document with an existing molecule.
+ */
+static VALUE
+s_Molecule_SetMolecule(VALUE self, VALUE mval)
+{
+ Molecule *mp1, *mp2;
+ Data_Get_Struct(self, Molecule, mp1);
+ mp2 = MoleculeFromValue(mval);
+ MoleculeInitWithMolecule(mp1, mp2);
+ MoleculeCallback_notifyModification(mp1, 1);
+ return self;
+}
+
+#pragma mark ------ Name attributes ------
+
+/*
+ * call-seq:
* name -> String
*
* Returns the display name of the molecule. If the molecule has no associated
if (buf[0] == 0)
return Qnil;
else
- return rb_str_new2(buf);
+ return Ruby_NewEncodedStringValue2(buf);
}
/*
p = strrchr(buf, '/');
if (p != NULL)
*p = 0;
- return rb_str_new2(buf);
+ return Ruby_NewEncodedStringValue2(buf);
}
}
if (buf[0] == 0) {
/* No associated document */
snprintf(buf, sizeof buf, "#<Molecule:0x%lx>", self);
- return rb_str_new2(buf);
+ return Ruby_NewEncodedStringValue2(buf);
} else {
/* Check whether the document name is duplicate */
char buf2[256];
} else {
snprintf(buf2, sizeof buf2, "Molecule[\"%s\"]", buf);
}
- return rb_str_new2(buf2);
- }
-}
-
-/*
- * call-seq:
- * open -> Molecule
- * open(file) -> Molecule
- *
- * Create a new molecule from file as a document. If file is not given, an untitled document is created.
- */
-static VALUE
-s_Molecule_Open(int argc, VALUE *argv, VALUE self)
-{
- VALUE fname;
- const char *p;
- Molecule *mp;
- VALUE iflag;
- rb_scan_args(argc, argv, "01", &fname);
- if (NIL_P(fname))
- p = NULL;
- else
- p = FileStringValuePtr(fname);
- iflag = Ruby_SetInterruptFlag(Qfalse);
- mp = MoleculeCallback_openNewMolecule(p);
- Ruby_SetInterruptFlag(iflag);
- if (mp == NULL) {
- if (p == NULL)
- rb_raise(rb_eMolbyError, "Cannot create untitled document");
- else
- rb_raise(rb_eMolbyError, "Cannot open the file %s", p);
+ return Ruby_NewEncodedStringValue2(buf2);
}
- return ValueFromMolecule(mp);
}
-/*
- * call-seq:
- * new -> Molecule
- * new(file, *args) -> Molecule
- *
- * Create a new molecule and call "load" method with the same arguments.
- */
-static VALUE
-s_Molecule_Initialize(int argc, VALUE *argv, VALUE self)
-{
- if (argc > 0)
- return s_Molecule_Load(argc, argv, self);
- else return Qnil; /* An empty molecule (which is prepared in s_Molecule_Alloc()) is returned */
-}
+#pragma mark ------ MolEnumerables ------
static VALUE
s_Molecule_MolEnumerable(VALUE self, int kind)
return INT2NUM(mol->nresidues);
}
-static VALUE
-s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
-{
- 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.");
-}
-
/*
* call-seq:
- * bond_par(idx) -> ParameterRef
+ * nresidues = Integer
*
- * Returns the MD parameter for the idx-th bond.
+ * Change the number of residues.
*/
-/*
static VALUE
-s_Molecule_BondPar(VALUE self, VALUE val)
+s_Molecule_ChangeNresidues(VALUE self, VALUE val)
{
Molecule *mol;
- BondPar *bp;
- UInt t1, t2;
- Int i1, i2;
- Int ival;
+ int ival = NUM2INT(val);
Data_Get_Struct(self, Molecule, mol);
- ival = NUM2INT(rb_Integer(val));
- if (ival < -mol->nbonds || ival >= mol->nbonds)
- rb_raise(rb_eMolbyError, "bond index (%d) out of range", ival);
- if (ival < 0)
- ival += mol->nbonds;
- s_RebuildMDParameterIfNecessary(self, Qtrue);
- i1 = mol->bonds[ival * 2];
- i2 = mol->bonds[ival * 2 + 1];
- t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
- t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
- bp = ParameterLookupBondPar(mol->par, t1, t2, i1, i2, 0);
- if (bp == NULL)
- return Qnil;
- return ValueFromMoleculeWithParameterTypeAndIndex(mol, kBondParType, bp - mol->par->bondPars);
+ MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
+ if (ival != mol->nresidues)
+ rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
+ return val;
}
-*/
/*
* call-seq:
- * angle_par(idx) -> ParameterRef
+ * max_residue_number(atom_group = nil) -> Integer
*
- * Returns the MD parameter for the idx-th angle.
+ * Returns the maximum residue number actually used. If an atom group is given, only
+ * these atoms are examined. If no atom is present, nil is returned.
*/
-/*
static VALUE
-s_Molecule_AnglePar(VALUE self, VALUE val)
+s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- AnglePar *ap;
- UInt t1, t2, t3;
- Int i1, i2, i3;
- Int ival;
+ VALUE gval;
+ int maxSeq;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- ival = NUM2INT(rb_Integer(val));
- if (ival < -mol->nangles || ival >= mol->nangles)
- rb_raise(rb_eMolbyError, "angle index (%d) out of range", ival);
- if (ival < 0)
- ival += mol->nangles;
- s_RebuildMDParameterIfNecessary(self, Qtrue);
- i1 = mol->angles[ival * 3];
- i2 = mol->angles[ival * 3 + 1];
- i3 = mol->angles[ival * 3 + 2];
- t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
- t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
- t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
- ap = ParameterLookupAnglePar(mol->par, t1, t2, t3, i1, i2, i3, 0);
- if (ap == NULL)
- return Qnil;
- return ValueFromMoleculeWithParameterTypeAndIndex(mol, kAngleParType, ap - mol->par->anglePars);
+ rb_scan_args(argc, argv, "01", &gval);
+ ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+ maxSeq = MoleculeMaximumResidueNumber(mol, ig);
+ return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
}
-*/
+
/*
* call-seq:
- * dihedral_par(idx) -> ParameterRef
+ * min_residue_number(atom_group = nil) -> Integer
*
- * Returns the MD parameter for the idx-th dihedral.
+ * Returns the minimum residue number actually used. If an atom group is given, only
+ * these atoms are examined. If no atom is present, nil is returned.
*/
-/*
static VALUE
-s_Molecule_DihedralPar(VALUE self, VALUE val)
+s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- Int ival;
- TorsionPar *tp;
- UInt t1, t2, t3, t4;
- Int i1, i2, i3, i4;
+ VALUE gval;
+ int minSeq;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- ival = NUM2INT(rb_Integer(val));
- if (ival < -mol->ndihedrals || ival >= mol->ndihedrals)
- rb_raise(rb_eMolbyError, "dihedral index (%d) out of range", ival);
- if (ival < 0)
- ival += mol->ndihedrals;
- s_RebuildMDParameterIfNecessary(self, Qtrue);
- i1 = mol->dihedrals[ival * 4];
- i2 = mol->dihedrals[ival * 4 + 1];
- i3 = mol->dihedrals[ival * 4 + 2];
- i4 = mol->dihedrals[ival * 4 + 3];
- t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
- t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
- t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
- t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
- tp = ParameterLookupDihedralPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
- if (tp == NULL)
- return Qnil;
- return ValueFromMoleculeWithParameterTypeAndIndex(mol, kDihedralParType, tp - mol->par->dihedralPars);
+ rb_scan_args(argc, argv, "01", &gval);
+ ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+ minSeq = MoleculeMinimumResidueNumber(mol, ig);
+ return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
}
-*/
+
/*
* call-seq:
- * improper_par(idx) -> ParameterRef
+ * each_atom(atom_group = nil) {|aref| ...}
*
- * Returns the MD parameter for the idx-th improper.
+ * Execute the block, with the AtomRef object for each atom as the argument. If an atom
+ * group is given, only these atoms are processed.
+ * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
+ * is self (a Molecule object).
*/
-/*
static VALUE
-s_Molecule_ImproperPar(VALUE self, VALUE val)
+s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
{
+ int i;
Molecule *mol;
- Int ival;
- TorsionPar *tp;
- UInt t1, t2, t3, t4;
- Int i1, i2, i3, i4;
+ AtomRef *aref;
+ VALUE arval;
+ VALUE gval;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- ival = NUM2INT(rb_Integer(val));
- if (ival < -mol->nimpropers || ival >= mol->nimpropers)
- rb_raise(rb_eMolbyError, "improper index (%d) out of range", ival);
- if (ival < 0)
- ival += mol->nimpropers;
- s_RebuildMDParameterIfNecessary(self, Qtrue);
- i1 = mol->impropers[ival * 4];
- i2 = mol->impropers[ival * 4 + 1];
- i3 = mol->impropers[ival * 4 + 2];
- i4 = mol->impropers[ival * 4 + 3];
- t1 = ATOM_AT_INDEX(mol->atoms, i1)->type;
- t2 = ATOM_AT_INDEX(mol->atoms, i2)->type;
- t3 = ATOM_AT_INDEX(mol->atoms, i3)->type;
- t4 = ATOM_AT_INDEX(mol->atoms, i4)->type;
- tp = ParameterLookupImproperPar(mol->par, t1, t2, t3, t4, i1, i2, i3, i4, 0);
- if (tp == NULL)
- return Qnil;
- return ValueFromMoleculeWithParameterTypeAndIndex(mol, kImproperParType, tp - mol->par->improperPars);
+ rb_scan_args(argc, argv, "01", &gval);
+ ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
+ arval = ValueFromMoleculeAndIndex(mol, 0);
+ Data_Get_Struct(arval, AtomRef, aref);
+ for (i = 0; i < mol->natoms; i++) {
+ aref->idx = i;
+ if (ig == NULL || IntGroupLookup(ig, i, NULL))
+ rb_yield(arval);
+ }
+ if (ig != NULL)
+ IntGroupRelease(ig);
+ return self;
}
-*/
-/*
- * call-seq:
- * start_step -> Integer
- *
- * Returns the start step (defined by dcd format).
- */
+#pragma mark ------ Atom Group ------
+
static VALUE
-s_Molecule_StartStep(VALUE self)
+s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
{
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(mol->startStep);
+ Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
+ IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
+ int idx = s_Molecule_AtomIndexFromValue(mol, arg);
+ IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
+ return Qnil;
}
/*
* call-seq:
- * start_step = Integer
+ * atom_group
+ * atom_group {|aref| ...}
+ * atom_group(arg1, arg2, ...)
+ * atom_group(arg1, arg2, ...) {|aref| ...}
+ *
+ * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
+ * If arguments are given, then the atoms reprensented by the arguments are added to the
+ * group. For a conversion of a string to an atom index, see the description
+ * of Molecule#atom_index.
+ * If a block is given, it is evaluated with an AtomRef (not atom index integers)
+ * representing each atom, and the atoms are removed from the result if the block returns false.
*
- * Set the start step (defined by dcd format).
*/
static VALUE
-s_Molecule_SetStartStep(VALUE self, VALUE val)
+s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
{
+ IntGroup *ig1, *ig2;
Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- mol->startStep = NUM2INT(rb_Integer(val));
- return val;
+ Int i, startPt, interval;
+ VALUE retval = IntGroup_Alloc(rb_cIntGroup);
+ Data_Get_Struct(retval, IntGroup, ig1);
+ Data_Get_Struct(self, Molecule, mol);
+ if (argc == 0) {
+ IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
+ } else {
+ while (argc > 0) {
+ if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
+ i = s_Molecule_AtomIndexFromValue(mol, *argv);
+ IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
+ } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
+ ig2 = IntGroupFromValue(*argv);
+ for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
+ interval = IntGroupGetInterval(ig2, i);
+ IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
+ }
+ IntGroupRelease(ig2);
+ } else if (rb_respond_to(*argv, rb_intern("each"))) {
+ VALUE values[2];
+ values[0] = (VALUE)mol;
+ values[1] = (VALUE)ig1;
+ rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
+ } else
+ IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
+ argc--;
+ argv++;
+ }
+ }
+ if (rb_block_given_p()) {
+ /* Evaluate the given block with an AtomRef as the argument, and delete
+ the index if the block returns false */
+ AtomRef *aref = AtomRefNew(mol, 0);
+ VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+ ig2 = IntGroupNew();
+ IntGroupCopy(ig2, ig1);
+ for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
+ VALUE resval;
+ if (startPt >= mol->natoms)
+ break;
+ aref->idx = startPt;
+ resval = rb_yield(arval);
+ if (!RTEST(resval))
+ IntGroupRemove(ig1, startPt, 1);
+ }
+ IntGroupRelease(ig2);
+ }
+
+ /* Remove points that are out of bounds */
+ IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
+
+ return retval;
}
/*
* call-seq:
- * steps_per_frame -> Integer
+ * selection -> IntGroup
*
- * Returns the number of steps between frames (defined by dcd format).
+ * Returns the current selection.
*/
static VALUE
-s_Molecule_StepsPerFrame(VALUE self)
+s_Molecule_Selection(VALUE self)
{
Molecule *mol;
+ IntGroup *ig;
+ VALUE val;
Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(mol->stepsPerFrame);
+ if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
+ ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
+ val = ValueFromIntGroup(ig);
+ IntGroupRelease(ig);
+ } else {
+ val = IntGroup_Alloc(rb_cIntGroup);
+ }
+ return val;
}
-/*
- * call-seq:
- * steps_per_frame = Integer
- *
- * Set the number of steps between frames (defined by dcd format).
- */
static VALUE
-s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
+s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
{
Molecule *mol;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- mol->stepsPerFrame = NUM2INT(rb_Integer(val));
+ if (val == Qnil)
+ ig = NULL;
+ else
+ ig = s_Molecule_AtomGroupFromValue(self, val);
+ if (undoable)
+ MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
+ else
+ MoleculeSetSelection(mol, ig);
+ if (ig != NULL)
+ IntGroupRelease(ig);
return val;
}
/*
* call-seq:
- * ps_per_step -> Float
+ * selection = IntGroup
*
- * Returns the time increment (in picoseconds) for one step (defined by dcd format).
+ * Set the current selection. The right-hand operand may be nil.
+ * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
*/
static VALUE
-s_Molecule_PsPerStep(VALUE self)
+s_Molecule_SetSelection(VALUE self, VALUE val)
{
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- return rb_float_new(mol->psPerStep);
+ return s_Molecule_SetSelectionSub(self, val, 0);
}
/*
* call-seq:
- * ps_per_step = Float
+ * set_undoable_selection(IntGroup)
*
- * Set the time increment (in picoseconds) for one step (defined by dcd format).
+ * Set the current selection with undo registration. The right-hand operand may be nil.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_SetPsPerStep(VALUE self, VALUE val)
+s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
{
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- mol->psPerStep = NUM2DBL(rb_Float(val));
- return val;
+ return s_Molecule_SetSelectionSub(self, val, 1);
}
+#pragma mark ------ Editing ------
+
/*
* call-seq:
- * find_angles -> Integer
+ * extract(group, dummy_flag = nil) -> Molecule
*
- * Find the angles from the bonds. Returns the number of angles newly created.
+ * Extract the atoms given by group and return as a new molecule object.
+ * If dummy_flag is true, then the atoms that are not included in the group but are connected
+ * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
+ * names beginning with an underscore) and included in the new molecule object.
*/
-/*
static VALUE
-s_Molecule_FindAngles(VALUE self)
+s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
{
- Molecule *mol;
- Atom *ap;
- int n1, i, j, nc;
- Int *ip, nip, n[3];
- Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL || mol->natoms == 0)
- return INT2NUM(0);
- ip = NULL;
- nip = 0;
- for (n1 = 0, ap = mol->atoms; n1 < mol->natoms; n1++, ap = ATOM_NEXT(ap)) {
- nc = ap->connect.count;
- n[1] = n1;
- for (i = 0; i < nc; i++) {
- n[0] = ap->connects[i];
- for (j = i + 1; j < nc; j++) {
- n[2] = ap->connects[j];
- if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) < 0)
- AssignArray(&ip, &nip, sizeof(Int) * 3, nip, n);
- }
- }
- }
- if (nip > 0) {
- MolActionCreateAndPerform(mol, gMolActionAddAngles, nip * 3, ip, NULL);
- free(ip);
+ Molecule *mol1, *mol2;
+ IntGroup *ig;
+ VALUE group, dummy_flag, retval;
+ Data_Get_Struct(self, Molecule, mol1);
+ rb_scan_args(argc, argv, "11", &group, &dummy_flag);
+ ig = s_Molecule_AtomGroupFromValue(self, group);
+ if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
+ retval = Qnil;
+ } else {
+ retval = ValueFromMolecule(mol2);
}
- return INT2NUM(nip);
+ IntGroupRelease(ig);
+ return retval;
}
-*/
+
/*
* call-seq:
- * find_dihedrals -> Integer
+ * add(molecule2) -> self
*
- * Find the dihedrals from the bonds. Returns the number of dihedrals newly created.
+ * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
+ conflicts.
+ This operation is undoable.
*/
+static VALUE
+s_Molecule_Add(VALUE self, VALUE val)
+{
+ Molecule *mol1, *mol2;
+ Data_Get_Struct(self, Molecule, mol1);
+ mol2 = MoleculeFromValue(val);
+ MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
+ return self;
+}
+
/*
+ * call-seq:
+ * remove(group) -> Molecule
+ *
+ * The atoms designated by the given group are removed from the molecule.
+ * This operation is undoable.
+ */
static VALUE
-s_Molecule_FindDihedrals(VALUE self)
+s_Molecule_Remove(VALUE self, VALUE group)
{
- Molecule *mol;
- Atom *ap1, *ap2;
- int n1, i, j, k, nc1, nc2;
- Int *ip, nip, n[4];
- Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL || mol->natoms == 0)
- return INT2NUM(0);
- ip = NULL;
- nip = 0;
- for (n1 = 0, ap1 = mol->atoms; n1 < mol->natoms; n1++, ap1 = ATOM_NEXT(ap1)) {
- nc1 = ap1->connect.count;
- n[1] = n1;
- for (i = 0; i < nc1; i++) {
- n[2] = ap1->connects[i];
- if (n[1] > n[2])
- continue;
- ap2 = ATOM_AT_INDEX(mol->atoms, n[2]);
- nc2 = ap2->connect.count;
- for (j = 0; j < nc1; j++) {
- n[0] = ap1->connects[j];
- if (n[0] == n[2])
- continue;
- for (k = 0; k < nc2; k++) {
- n[3] = ap2->connects[k];
- if (n[3] == n1 || n[3] == n[0])
- continue;
- if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) < 0)
- AssignArray(&ip, &nip, sizeof(Int) * 4, nip, n);
+ Molecule *mol1;
+ IntGroup *ig, *bg;
+ Int i;
+ IntGroupIterator iter;
+
+ ig = s_Molecule_AtomGroupFromValue(self, group);
+/* Data_Get_Struct(self, Molecule, mol1);
+ group = rb_funcall(self, rb_intern("atom_group"), 1, group);
+ if (!rb_obj_is_kind_of(group, rb_cIntGroup))
+ rb_raise(rb_eMolbyError, "IntGroup instance is expected");
+ Data_Get_Struct(group, IntGroup, ig); */
+ Data_Get_Struct(self, Molecule, mol1);
+
+ /* Remove the bonds between the two fragments */
+ /* (This is necessary for undo to work correctly) */
+ IntGroupIteratorInit(ig, &iter);
+ bg = NULL;
+ while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+ Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
+ Int j, *cp;
+ cp = AtomConnectData(&ap->connect);
+ for (j = 0; j < ap->connect.count; j++) {
+ int n = cp[j];
+ if (!IntGroupLookup(ig, n, NULL)) {
+ /* bond i-n, i is in ig and n is not */
+ int k = MoleculeLookupBond(mol1, i, n);
+ if (k >= 0) {
+ if (bg == NULL)
+ bg = IntGroupNew();
+ IntGroupAdd(bg, k, 1);
}
}
}
}
- if (nip > 0) {
- MolActionCreateAndPerform(mol, gMolActionAddDihedrals, nip * 4, ip, NULL);
- free(ip);
+ IntGroupIteratorRelease(&iter);
+ if (bg != NULL) {
+ /* Remove bonds */
+ MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
+ IntGroupRelease(bg);
}
- return INT2NUM(nip);
+ /* Remove atoms */
+ if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
+ return Qnil;
+ return self;
}
-*/
/*
* call-seq:
- * nresidues = Integer
+ * create_atom(name, pos = -1) -> AtomRef
*
- * Change the number of residues.
+ * Create a new atom with the specified name (may contain residue
+ * information) and position (if position is out of range, the atom is appended at
+ * the end). Returns the reference to the new atom.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_ChangeNresidues(VALUE self, VALUE val)
+s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- int ival = NUM2INT(val);
+ Int i, pos;
+ VALUE name, ival;
+ Atom arec;
+ AtomRef *aref;
+ char *p, resName[6], atomName[6];
+ int resSeq;
Data_Get_Struct(self, Molecule, mol);
- MolActionCreateAndPerform(mol, gMolActionChangeNumberOfResidues, ival);
- if (ival != mol->nresidues)
- rb_raise(rb_eMolbyError, "Cannot set the number of residues to %d (set to %d)", ival, mol->nresidues);
- return val;
+ rb_scan_args(argc, argv, "02", &name, &ival);
+ if (ival != Qnil)
+ pos = NUM2INT(rb_Integer(ival));
+ else pos = -1;
+ if (name != Qnil) {
+ p = StringValuePtr(name);
+ if (p[0] != 0) {
+ i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
+ if (atomName[0] == 0)
+ rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
+ }
+ } else p = NULL;
+ if (p == NULL || p[0] == 0) {
+ memset(atomName, 0, 4);
+ resSeq = -1;
+ }
+ memset(&arec, 0, sizeof(arec));
+ strncpy(arec.aname, atomName, 4);
+ if (resSeq >= 0) {
+ strncpy(arec.resName, resName, 4);
+ arec.resSeq = resSeq;
+ }
+ arec.occupancy = 1.0;
+// i = MoleculeCreateAnAtom(mol, &arec);
+ if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
+ return Qnil;
+ aref = AtomRefNew(mol, pos);
+ return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
}
/*
* call-seq:
- * max_residue_number(atom_group = nil) -> Integer
+ * duplicate_atom(atomref, pos = -1) -> AtomRef
*
- * Returns the maximum residue number actually used. If an atom group is given, only
- * these atoms are examined. If no atom is present, nil is returned.
- */
+ * Create a new atom with the same attributes (but no bonding information)
+ * with the specified atom. Returns the reference to the new atom.
+ * This operation is undoable.
+ */
static VALUE
-s_Molecule_MaxResSeq(int argc, VALUE *argv, VALUE self)
+s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE gval;
- int maxSeq;
- IntGroup *ig;
+ const Atom *apsrc;
+ Atom arec;
+ AtomRef *aref;
+ VALUE retval, aval, ival;
+ Int pos;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &gval);
- ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
- maxSeq = MoleculeMaximumResidueNumber(mol, ig);
- return (maxSeq >= 0 ? INT2NUM(maxSeq) : Qnil);
+ rb_scan_args(argc, argv, "11", &aval, &ival);
+ if (FIXNUM_P(aval)) {
+ int idx = NUM2INT(aval);
+ if (idx < 0 || idx >= mol->natoms)
+ rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
+ apsrc = ATOM_AT_INDEX(mol->atoms, idx);
+ } else {
+ apsrc = s_AtomFromValue(aval);
+ }
+ if (apsrc == NULL)
+ rb_raise(rb_eMolbyError, "bad atom specification");
+ if (ival != Qnil)
+ pos = NUM2INT(rb_Integer(ival));
+ else pos = -1;
+ AtomDuplicate(&arec, apsrc);
+ arec.connect.count = 0;
+ if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
+ retval = Qnil;
+ else {
+ aref = AtomRefNew(mol, pos);
+ retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
+ }
+ AtomClean(&arec);
+ return retval;
}
/*
* call-seq:
- * min_residue_number(atom_group = nil) -> Integer
+ * create_bond(n1, n2, ...) -> Integer
*
- * Returns the minimum residue number actually used. If an atom group is given, only
- * these atoms are examined. If no atom is present, nil is returned.
+ * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
+ * do nothing for that pair. Returns the number of bonds actually created.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_MinResSeq(int argc, VALUE *argv, VALUE self)
+s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE gval;
- int minSeq;
- IntGroup *ig;
+ Int i, j, k, *ip, old_nbonds;
+ if (argc == 0)
+ rb_raise(rb_eMolbyError, "missing arguments");
+ if (argc % 2 != 0)
+ rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &gval);
- ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
- minSeq = MoleculeMinimumResidueNumber(mol, ig);
- return (minSeq >= 0 ? INT2NUM(minSeq) : Qnil);
+ ip = ALLOC_N(Int, argc + 1);
+ for (i = j = 0; i < argc; i++, j++) {
+ ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
+ if (i % 2 == 1) {
+ if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
+ j -= 2; /* This bond is already present: skip it */
+ else {
+ for (k = 0; k < j - 1; k += 2) {
+ if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
+ j -= 2; /* The same entry is already in the argument */
+ break;
+ }
+ }
+ }
+ }
+ }
+ old_nbonds = mol->nbonds;
+ if (j > 0) {
+ ip[j] = kInvalidIndex;
+ i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
+ } else i = 0;
+ xfree(ip);
+ if (i == -1)
+ rb_raise(rb_eMolbyError, "atom index out of range");
+ else if (i == -2)
+ rb_raise(rb_eMolbyError, "too many bonds");
+ else if (i == -3)
+ rb_raise(rb_eMolbyError, "duplicate bonds");
+ else if (i == -5)
+ rb_raise(rb_eMolbyError, "cannot create bond to itself");
+ else if (i != 0)
+ rb_raise(rb_eMolbyError, "error in creating bonds");
+ return INT2NUM(mol->nbonds - old_nbonds);
}
/*
* call-seq:
- * each_atom(atom_group = nil) {|aref| ...}
+ * molecule.remove_bonds(n1, n2, ...) -> Integer
*
- * Execute the block, with the AtomRef object for each atom as the argument. If an atom
- * group is given, only these atoms are processed.
- * If atom_group is nil, this is equivalent to self.atoms.each, except that the return value
- * is self (a Molecule object).
+ * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
+ * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_EachAtom(int argc, VALUE *argv, VALUE self)
+s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
{
- int i;
Molecule *mol;
- AtomRef *aref;
- VALUE arval;
- VALUE gval;
- IntGroup *ig;
+ Int i, j, n[2];
+ IntGroup *bg;
+ if (argc == 0)
+ rb_raise(rb_eMolbyError, "missing arguments");
+ if (argc % 2 != 0)
+ rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &gval);
- ig = (gval == Qnil ? NULL : s_Molecule_AtomGroupFromValue(self, gval));
- arval = ValueFromMoleculeAndIndex(mol, 0);
- Data_Get_Struct(arval, AtomRef, aref);
- for (i = 0; i < mol->natoms; i++) {
- aref->idx = i;
- if (ig == NULL || IntGroupLookup(ig, i, NULL))
- rb_yield(arval);
+ bg = NULL;
+ for (i = j = 0; i < argc; i++, j = 1 - j) {
+ n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
+ if (j == 1) {
+ Int k = MoleculeLookupBond(mol, n[0], n[1]);
+ if (k >= 0) {
+ if (bg == NULL)
+ bg = IntGroupNew();
+ IntGroupAdd(bg, k, 1);
+ }
+ }
}
- if (ig != NULL)
- IntGroupRelease(ig);
- return self;
+ if (bg != NULL) {
+ MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
+ i = IntGroupGetCount(bg);
+ IntGroupRelease(bg);
+ } else i = 0;
+ return INT2NUM(i);
}
/*
* call-seq:
- * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
+ * assign_bond_order(idx, d1)
+ * assign_bond_orders(group, [d1, d2, ...])
*
- * Returns the unit cell parameters. If cell is not set, returns nil.
+ * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
+ * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
+ * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
+ * (This may change in the future)
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_Cell(VALUE self)
+s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
{
Molecule *mol;
- int i;
- VALUE val;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- return Qnil;
- val = rb_ary_new2(6);
- for (i = 0; i < 6; i++)
- rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
- if (mol->cell->has_sigma) {
- for (i = 0; i < 6; i++) {
- rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
+ if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
+ /* The first form */
+ Int idx = NUM2INT(rb_Integer(idxval));
+ Double d1 = NUM2DBL(rb_Float(dval));
+ if (idx < 0 || idx >= mol->nbonds)
+ rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
+ ig = IntGroupNewWithPoints(idx, 1, -1);
+ MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
+ IntGroupRelease(ig);
+ } else {
+ Int i, n;
+ Double *dp;
+ ig = IntGroupFromValue(idxval);
+ n = IntGroupGetCount(ig);
+ if (n == 0)
+ rb_raise(rb_eMolbyError, "the bond index is empty");
+ dval = rb_ary_to_ary(dval);
+ dp = (Double *)calloc(sizeof(Double), n);
+ for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
+ dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
}
+ MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
+ free(dp);
+ IntGroupRelease(ig);
}
- return val;
+ return self;
}
/*
* call-seq:
- * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
- * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
+ * get_bond_order(idx) -> Float
+ * get_bond_orders(group) -> Array
*
- * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
- If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
- This operation is undoable.
- Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
+ * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
+ * In the second form, the bond orders at the indices in the group are returned as an array.
+ * If no bond order information have been assigned, returns nil (the first form)
+ * or an empty array (the second form).
*/
static VALUE
-s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
+s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
{
Molecule *mol;
- VALUE val, cval;
- int i, convert_coord, n;
- double d[12];
+ IntGroup *ig;
+ Double *dp;
+ VALUE retval;
+ Int i, n, numericArg;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "11", &val, &cval);
- if (val == Qnil) {
- n = 0;
+ if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
+ /* The first form */
+ Int idx = NUM2INT(rb_Integer(idxval));
+ if (idx < 0 || idx >= mol->nbonds)
+ rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
+ if (mol->bondOrders == NULL)
+ return Qnil;
+ ig = IntGroupNewWithPoints(idx, 1, -1);
+ n = 1;
+ numericArg = 1;
} else {
- int len;
- val = rb_ary_to_ary(val);
- len = RARRAY_LEN(val);
- if (len >= 12) {
- n = 12;
- } else if (len >= 6) {
- n = 6;
- } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
+ if (mol->bondOrders == NULL)
+ return rb_ary_new();
+ ig = IntGroupFromValue(idxval);
+ n = IntGroupGetCount(ig);
+ if (n == 0)
+ rb_raise(rb_eMolbyError, "the bond index is empty");
+ numericArg = 0;
+ }
+ dp = (Double *)calloc(sizeof(Double), n);
+ MoleculeGetBondOrders(mol, dp, ig);
+ if (numericArg)
+ retval = rb_float_new(dp[0]);
+ else {
+ retval = rb_ary_new();
for (i = 0; i < n; i++)
- d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
+ rb_ary_push(retval, rb_float_new(dp[i]));
}
- convert_coord = (RTEST(cval) ? 1 : 0);
- MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
- return val;
+ free(dp);
+ IntGroupRelease(ig);
+ return retval;
}
/*
* call-seq:
- * box -> [avec, bvec, cvec, origin, flags]
+ * bond_exist?(idx1, idx2) -> bool
*
- * Get the unit cell information in the form of a periodic bounding box.
- * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
- * Integers which define whether the system is periodic along the axis.
- * If no unit cell is defined, nil is returned.
+ * Returns true if bond exists between atoms idx1 and idx2, otherwise returns false.
+ * Imaginary bonds between a pi-anchor and member atoms are not considered.
*/
static VALUE
-s_Molecule_Box(VALUE self)
+s_Molecule_BondExist(VALUE self, VALUE ival1, VALUE ival2)
{
- Molecule *mol;
- VALUE v[5], val;
+ Molecule *mol;
+ Int idx1, idx2, i;
+ Atom *ap;
+ Int *cp;
Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL || mol->cell == NULL)
- return Qnil;
- v[0] = ValueFromVector(&(mol->cell->axes[0]));
- v[1] = ValueFromVector(&(mol->cell->axes[1]));
- v[2] = ValueFromVector(&(mol->cell->axes[2]));
- v[3] = ValueFromVector(&(mol->cell->origin));
- v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
- val = rb_ary_new4(5, v);
- return val;
+ idx1 = NUM2INT(rb_Integer(ival1));
+ idx2 = NUM2INT(rb_Integer(ival2));
+ if (idx1 < 0 || idx1 >= mol->natoms || idx2 < 0 || idx2 >= mol->natoms)
+ rb_raise(rb_eMolbyError, "Atom index (%d or %d) out of range", idx1, idx2);
+ ap = ATOM_AT_INDEX(mol->atoms, idx1);
+ cp = AtomConnectData(&ap->connect);
+ for (i = 0; i < ap->connect.count; i++) {
+ if (cp[i] == idx2)
+ return Qtrue;
+ }
+ return Qfalse;
}
/*
* call-seq:
- * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
- * set_box(d, origin = [0, 0, 0])
- * set_box
+ * add_angle(n1, n2, n3) -> Molecule
*
- * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
- If it is a number, the x/y/z axis vector is multiplied with the given number and used
- as the box vector.
- Flags, if present, is a 3-member array of Integers defining whether the system is
- periodic along the axis.
- If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
- In the second form, an isotropic box with cell-length d is set.
- In the third form, the existing box is cleared.
- Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
+ * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
+ * when a bond is created, so it is rarely necessary to use this method explicitly.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_SetBox(VALUE self, VALUE aval)
+s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
{
+ Int n[4];
Molecule *mol;
- VALUE v[6];
- static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
- Vector vv[3];
- Vector origin = {0, 0, 0};
- char flags[3];
- Double d;
- int i, convertCoordinates = 0;
Data_Get_Struct(self, Molecule, mol);
- if (aval == Qnil) {
- MolActionCreateAndPerform(mol, gMolActionClearBox);
- return self;
- }
- aval = rb_ary_to_ary(aval);
- for (i = 0; i < 6; i++) {
- if (i < RARRAY_LEN(aval))
- v[i] = (RARRAY_PTR(aval))[i];
- else v[i] = Qnil;
- }
- if (v[0] == Qnil) {
- MolActionCreateAndPerform(mol, gMolActionClearBox);
- return self;
- }
- if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
- d = NUM2DBL(rb_Float(v[0]));
- for (i = 0; i < 3; i++)
- VecScale(vv[i], ax[i], d);
- if (v[1] != Qnil)
- VectorFromValue(v[1], &origin);
- flags[0] = flags[1] = flags[2] = 1;
- } else {
- for (i = 0; i < 3; i++) {
- if (v[i] == Qnil) {
- VecZero(vv[i]);
- } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
- d = NUM2DBL(rb_Float(v[i]));
- VecScale(vv[i], ax[i], d);
- } else {
- VectorFromValue(v[i], &vv[i]);
- }
- flags[i] = (VecLength2(vv[i]) > 0.0);
- }
- if (v[3] != Qnil)
- VectorFromValue(v[3], &origin);
- if (v[4] != Qnil) {
- for (i = 0; i < 3; i++) {
- VALUE val = Ruby_ObjectAtIndex(v[4], i);
- flags[i] = (NUM2INT(rb_Integer(val)) != 0);
- }
- }
- if (RTEST(v[5]))
- convertCoordinates = 1;
- }
- MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
+ rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
+ n[3] = kInvalidIndex;
+ MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
return self;
}
/*
* call-seq:
- * cell_periodicity -> [n1, n2, n3]
+ * remove_angle(n1, n2, n3) -> Molecule
*
- * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
- * nil is returned.
+ * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
+ * when a bond is removed, so it is rarely necessary to use this method explicitly.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_CellPeriodicity(VALUE self)
+s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
{
+ Int n[4];
Molecule *mol;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- return Qnil;
- return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
+ rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
+ ig = IntGroupNewWithPoints(n[3], 1, -1);
+ MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
+ IntGroupRelease(ig);
+ return self;
}
/*
* call-seq:
- * self.cell_periodicity = [n1, n2, n3] or Integer or nil
- * set_cell_periodicity = [n1, n2, n3] or Integer or nil
+ * add_dihedral(n1, n2, n3, n4) -> Molecule
*
- * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
- * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
- * If cell is not defined, exception is raised.
+ * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
+ * when a bond is created, so it is rarely necessary to use this method explicitly.
* This operation is undoable.
*/
static VALUE
-s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
+s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
{
+ Int n[5];
Molecule *mol;
- Int flag;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- rb_raise(rb_eMolbyError, "periodic cell is not defined");
- if (arg == Qnil)
- flag = 0;
- else if (rb_obj_is_kind_of(arg, rb_cNumeric))
- flag = NUM2INT(rb_Integer(arg));
- else {
- Int i;
- VALUE arg0;
- arg = rb_ary_to_ary(arg);
- flag = 0;
- for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
- arg0 = RARRAY_PTR(arg)[i];
- if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
- flag |= (1 << (2 - i));
- }
- }
- MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
- return arg;
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+ if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
+ rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
+ n[4] = kInvalidIndex;
+ MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
+ return self;
}
/*
* call-seq:
- * cell_flexibility -> bool
+ * remove_dihedral(n1, n2, n3, n4) -> Molecule
*
- * Returns the unit cell is flexible or not
+ * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
+ * when a bond is removed, so it is rarely necessary to use this method explicitly.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_CellFlexibility(VALUE self)
+s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
{
- rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
- return Qtrue;
-/* Molecule *mol;
+ Int n[5];
+ Molecule *mol;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- return Qfalse;
- if (mol->useFlexibleCell)
- return Qtrue;
- else return Qfalse; */
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+ if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
+ rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
+ ig = IntGroupNewWithPoints(n[4], 1, -1);
+ MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
+ IntGroupRelease(ig);
+ return self;
}
/*
* call-seq:
- * self.cell_flexibility = bool
- * set_cell_flexibility(bool)
+ * add_improper(n1, n2, n3, n4) -> Molecule
*
- * Change the unit cell is flexible or not
+ * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
+ * not automatically added when a new bond is created, so this method is more useful than
+ * the angle/dihedral counterpart.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
+s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
{
- rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
- return self;
-/* Molecule *mol;
+ Int n[5];
+ Molecule *mol;
Data_Get_Struct(self, Molecule, mol);
- MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
- return self; */
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+ if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
+ rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
+ n[4] = kInvalidIndex;
+ MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
+ return self;
}
/*
* call-seq:
- * cell_transform -> Transform
+ * remove_improper(n1, n2, n3, n4) -> Molecule
+ * remove_improper(intgroup) -> Molecule
*
- * Get the transform matrix that converts internal coordinates to cartesian coordinates.
- * If cell is not defined, nil is returned.
+ * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
+ * Returns self. Unlike angles and dihedrals, impropers are
+ * not automatically added when a new bond is created, so this method is more useful than
+ * the angle/dihedral counterpart.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_CellTransform(VALUE self)
+s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
{
+ Int n[5];
+ VALUE v1, v2, v3, v4;
Molecule *mol;
+ IntGroup *ig;
Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL || mol->cell == NULL)
- return Qnil;
- return ValueFromTransform(&(mol->cell->tr));
+ if (argc == 1) {
+ ig = IntGroupFromValue(argv[0]);
+ } else {
+ rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
+ n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
+ n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
+ n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
+ n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
+ if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
+ rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
+ ig = IntGroupNewWithPoints(n[4], 1, -1);
+ }
+ MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
+ IntGroupRelease(ig);
+ return self;
}
/*
* call-seq:
- * symmetry -> Array of Transforms
- * symmetries -> Array of Transforms
+ * assign_residue(group, res) -> Molecule
*
- * Get the currently defined symmetry operations. If no symmetry operation is defined,
- * returns an empty array.
+ * Assign the specified atoms as the given residue. res can either be an integer, "resname"
+ * or "resname.resno". When the residue number is not specified, the residue number of
+ * the first atom in the group is used.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_Symmetry(VALUE self)
+s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
{
Molecule *mol;
- VALUE val;
- int i;
- Data_Get_Struct(self, Molecule, mol);
- if (mol->nsyms <= 0)
- return rb_ary_new();
- val = rb_ary_new2(mol->nsyms);
- for (i = 0; i < mol->nsyms; i++) {
- rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
+ IntGroup *ig;
+ char *p, *pp, buf[16];
+ Int resid, n;
+ Atom *ap;
+ Data_Get_Struct(self, Molecule, mol);
+
+ /* Parse the argument res */
+ if (FIXNUM_P(res)) {
+ /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
+ resid = NUM2INT(res);
+ buf[0] = 0;
+ } else {
+ p = StringValuePtr(res);
+ pp = strchr(p, '.');
+ if (pp != NULL) {
+ resid = atoi(pp + 1);
+ n = pp - p;
+ } else {
+ resid = -1;
+ n = strlen(p);
+ }
+ if (n > sizeof buf - 1)
+ n = sizeof buf - 1;
+ strncpy(buf, p, n);
+ buf[n] = 0;
}
- return val;
+ ig = s_Molecule_AtomGroupFromValue(self, range);
+ if (ig == NULL || IntGroupGetCount(ig) == 0)
+ return Qnil;
+
+ if (resid < 0) {
+ /* Use the residue number of the first specified atom */
+ n = IntGroupGetNthPoint(ig, 0);
+ if (n >= mol->natoms)
+ rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
+ ap = ATOM_AT_INDEX(mol->atoms, n);
+ resid = ap->resSeq;
+ }
+ /* Change the residue number */
+ MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
+ /* Change the residue name if necessary */
+ if (buf[0] != 0) {
+ /* Int seqs[2];
+ seqs[0] = resid;
+ seqs[1] = kInvalidIndex; */
+ MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
+ }
+ IntGroupRelease(ig);
+ return self;
}
/*
* call-seq:
- * nsymmetries -> Integer
+ * offset_residue(group, offset) -> Molecule
*
- * Get the number of currently defined symmetry operations.
+ * Offset the residue number of the specified atoms. If any of the residue number gets
+ * negative, then exception is thrown.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_Nsymmetries(VALUE self)
+s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
{
Molecule *mol;
+ IntGroup *ig;
+ int ofs, result;
Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(mol->nsyms);
+ ig = s_Molecule_AtomGroupFromValue(self, range);
+ ofs = NUM2INT(offset);
+ result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
+ if (result > 0)
+ rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
+ IntGroupRelease(ig);
+ return self;
}
/*
* call-seq:
- * add_symmetry(Transform) -> Integer
+ * renumber_atoms(array) -> IntGroup
*
- * Add a new symmetry operation. If no symmetry operation is defined and the
- * given argument is not an identity transform, then also add an identity
- * transform at the index 0.
- * Returns the total number of symmetries after operation.
+ * Change the order of atoms so that the atoms specified in the array argument appear
+ * in this order from the top of the molecule. The atoms that are not included in array
+ * are placed after these atoms, and these atoms are returned as an intGroup.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_AddSymmetry(VALUE self, VALUE trans)
+s_Molecule_RenumberAtoms(VALUE self, VALUE array)
{
Molecule *mol;
- Transform tr;
+ Int *new2old;
+ IntGroup *ig;
+ int i, n;
+ VALUE *valp, retval;
Data_Get_Struct(self, Molecule, mol);
- TransformFromValue(trans, &tr);
- MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
- return INT2NUM(mol->nsyms);
+ if (TYPE(array) != T_ARRAY)
+ array = rb_funcall(array, rb_intern("to_a"), 0);
+ n = RARRAY_LEN(array);
+ valp = RARRAY_PTR(array);
+ new2old = ALLOC_N(Int, n + 1);
+ for (i = 0; i < n; i++)
+ new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
+ new2old[i] = kInvalidIndex;
+ i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
+ if (i == 1)
+ rb_raise(rb_eMolbyError, "Atom index out of range");
+ else if (i == 2)
+ rb_raise(rb_eMolbyError, "Duplicate entry");
+ else if (i == 3)
+ rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
+ retval = IntGroup_Alloc(rb_cIntGroup);
+ Data_Get_Struct(retval, IntGroup, ig);
+ if (mol->natoms > n)
+ IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
+ xfree(new2old);
+ return retval;
}
/*
* call-seq:
- * remove_symmetry(count = nil) -> Integer
- * remove_symmetries(count = nil) -> Integer
+ * set_atom_attr(index, key, value)
*
- * Remove the specified number of symmetry operations. The last added ones are removed
- * first. If count is nil, then all symmetry operations are removed. Returns the
- * number of leftover symmetries.
+ * Set the atom attribute for the specified atom.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
+s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
{
- Molecule *mol;
- VALUE cval;
- int i, n;
+ Molecule *mol;
+ VALUE aref, oldval;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &cval);
- if (cval == Qnil)
- n = mol->nsyms - 1;
- else {
- n = NUM2INT(rb_Integer(cval));
- if (n < 0 || n > mol->nsyms)
- rb_raise(rb_eMolbyError, "the given count of symops is out of range");
- if (n == mol->nsyms)
- n = mol->nsyms - 1;
- }
- for (i = 0; i < n; i++)
- MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
- return INT2NUM(mol->nsyms);
+ aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
+ oldval = s_AtomRef_GetAttr(aref, key);
+ if (val == Qundef)
+ return oldval;
+ s_AtomRef_SetAttr(aref, key, val);
+ return val;
}
+/*
+ * call-seq:
+ * get_atom_attr(index, key)
+ *
+ * Get the atom attribute for the specified atom.
+ */
static VALUE
-s_Molecule_AtomGroup_i(VALUE arg, VALUE values)
+s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
{
- Molecule *mol = (Molecule *)(((VALUE *)values)[0]);
- IntGroup *ig1 = (IntGroup *)(((VALUE *)values)[1]);
- int idx = s_Molecule_AtomIndexFromValue(mol, arg);
- IntGroup_RaiseIfError(IntGroupAdd(ig1, idx, 1));
- return Qnil;
+ return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
}
+#pragma mark ------ Undo Support ------
+
/*
* call-seq:
- * atom_group
- * atom_group {|aref| ...}
- * atom_group(arg1, arg2, ...)
- * atom_group(arg1, arg2, ...) {|aref| ...}
- *
- * Specify a group of atoms. If no arguments are given, IntGroup\[0...natoms] is the result.
- * If arguments are given, then the atoms reprensented by the arguments are added to the
- * group. For a conversion of a string to an atom index, see the description
- * of Molecule#atom_index.
- * If a block is given, it is evaluated with an AtomRef (not atom index integers)
- * representing each atom, and the atoms are removed from the result if the block returns false.
+ * register_undo(script, *args)
*
+ * Register an undo operation with the current molecule.
*/
static VALUE
-s_Molecule_AtomGroup(int argc, VALUE *argv, VALUE self)
+s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
{
- IntGroup *ig1, *ig2;
- Molecule *mol;
- Int i, startPt, interval;
- VALUE retval = IntGroup_Alloc(rb_cIntGroup);
- Data_Get_Struct(retval, IntGroup, ig1);
+ Molecule *mol;
+ VALUE script, args;
+ MolAction *act;
Data_Get_Struct(self, Molecule, mol);
- if (argc == 0) {
- IntGroup_RaiseIfError(IntGroupAdd(ig1, 0, mol->natoms));
- } else {
- while (argc > 0) {
- if (FIXNUM_P(*argv) || TYPE(*argv) == T_STRING) {
- i = s_Molecule_AtomIndexFromValue(mol, *argv);
- IntGroup_RaiseIfError(IntGroupAdd(ig1, i, 1));
- } else if (rb_obj_is_kind_of(*argv, rb_cIntGroup)) {
- ig2 = IntGroupFromValue(*argv);
- for (i = 0; (startPt = IntGroupGetStartPoint(ig2, i)) >= 0; i++) {
- interval = IntGroupGetInterval(ig2, i);
- IntGroup_RaiseIfError(IntGroupAdd(ig1, startPt, interval));
- }
- IntGroupRelease(ig2);
- } else if (rb_respond_to(*argv, rb_intern("each"))) {
- VALUE values[2];
- values[0] = (VALUE)mol;
- values[1] = (VALUE)ig1;
- rb_iterate(rb_each, *argv, s_Molecule_AtomGroup_i, (VALUE)values);
- } else
- IntGroup_RaiseIfError(IntGroupAdd(ig1, NUM2INT(*argv), 1));
- argc--;
- argv++;
- }
- }
- if (rb_block_given_p()) {
- /* Evaluate the given block with an AtomRef as the argument, and delete
- the index if the block returns false */
- AtomRef *aref = AtomRefNew(mol, 0);
- VALUE arval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
- ig2 = IntGroupNew();
- IntGroupCopy(ig2, ig1);
- for (i = 0; (startPt = IntGroupGetNthPoint(ig2, i)) >= 0; i++) {
- VALUE resval;
- if (startPt >= mol->natoms)
- break;
- aref->idx = startPt;
- resval = rb_yield(arval);
- if (!RTEST(resval))
- IntGroupRemove(ig1, startPt, 1);
- }
- IntGroupRelease(ig2);
- }
-
- /* Remove points that are out of bounds */
- IntGroup_RaiseIfError(IntGroupRemove(ig1, mol->natoms, INT_MAX));
-
- return retval;
+ rb_scan_args(argc, argv, "1*", &script, &args);
+ act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
+ MolActionCallback_registerUndo(mol, act);
+ return script;
}
/*
* call-seq:
- * atom_index(val) -> Integer
+ * undo_enabled? -> bool
*
- * Returns the atom index represented by val. val can be either a non-negative integer
- * (directly representing the atom index), a negative integer (representing <code>natoms - val</code>),
- * a string of type "resname.resid:name" or "resname:name" or "resid:name" or "name",
- * where resname, resid, name are the residue name, residue id, and atom name respectively.
- * If val is a string and multiple atoms match the description, the atom with the lowest index
- * is returned.
+ * Returns true if undo is enabled for this molecule; otherwise no.
*/
static VALUE
-s_Molecule_AtomIndex(VALUE self, VALUE val)
+s_Molecule_UndoEnabled(VALUE self)
{
Molecule *mol;
Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(s_Molecule_AtomIndexFromValue(mol, val));
+ if (MolActionCallback_isUndoRegistrationEnabled(mol))
+ return Qtrue;
+ else return Qfalse;
}
/*
* call-seq:
- * extract(group, dummy_flag = nil) -> Molecule
+ * undo_enabled = bool
*
- * Extract the atoms given by group and return as a new molecule object.
- * If dummy_flag is true, then the atoms that are not included in the group but are connected
- * to any atoms in the group are converted to "dummy" atoms (i.e. with element "Du" and
- * names beginning with an underscore) and included in the new molecule object.
+ * Enable or disable undo.
*/
static VALUE
-s_Molecule_Extract(int argc, VALUE *argv, VALUE self)
+s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
{
- Molecule *mol1, *mol2;
- IntGroup *ig;
- VALUE group, dummy_flag, retval;
- Data_Get_Struct(self, Molecule, mol1);
- rb_scan_args(argc, argv, "11", &group, &dummy_flag);
- ig = s_Molecule_AtomGroupFromValue(self, group);
- if (MoleculeExtract(mol1, &mol2, ig, (dummy_flag != Qnil && dummy_flag != Qfalse)) != 0) {
- retval = Qnil;
- } else {
- retval = ValueFromMolecule(mol2);
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
+ return val;
+}
+
+#pragma mark ------ Measure ------
+
+static void
+s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
+{
+ switch (MoleculeCenterOfMass(mol, outv, ig)) {
+ case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
+ case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
+ case 0: break;
+ default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
}
- IntGroupRelease(ig);
- return retval;
}
/*
* call-seq:
- * add(molecule2) -> self
+ * center_of_mass(group = nil) -> Vector3D
*
- * Combine two molecules. The residue numbers of the newly added atoms may be renumbered to avoid
- conflicts.
- This operation is undoable.
+ * Calculate the center of mass for the given set of atoms. The argument
+ * group is null, then all atoms are considered.
*/
static VALUE
-s_Molecule_Add(VALUE self, VALUE val)
+s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
{
- Molecule *mol1, *mol2;
- Data_Get_Struct(self, Molecule, mol1);
- mol2 = MoleculeFromValue(val);
- MolActionCreateAndPerform(mol1, gMolActionMergeMolecule, mol2, NULL);
- return self;
+ Molecule *mol;
+ VALUE group;
+ IntGroup *ig;
+ Vector v;
+ Data_Get_Struct(self, Molecule, mol);
+ rb_scan_args(argc, argv, "01", &group);
+ ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+ s_Molecule_DoCenterOfMass(mol, &v, ig);
+ if (ig != NULL)
+ IntGroupRelease(ig);
+ return ValueFromVector(&v);
}
/*
* call-seq:
- * remove(group) -> Molecule
+ * centralize(group = nil) -> self
*
- * The atoms designated by the given group are removed from the molecule.
- * This operation is undoable.
+ * Translate the molecule so that the center of mass of the given group is located
+ * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
*/
static VALUE
-s_Molecule_Remove(VALUE self, VALUE group)
+s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
{
- Molecule *mol1;
- IntGroup *ig, *bg;
- Int i;
- IntGroupIterator iter;
-
- Data_Get_Struct(self, Molecule, mol1);
- group = rb_funcall(self, rb_intern("atom_group"), 1, group);
- if (!rb_obj_is_kind_of(group, rb_cIntGroup))
- rb_raise(rb_eMolbyError, "IntGroup instance is expected");
- Data_Get_Struct(group, IntGroup, ig);
-
- /* Remove the bonds between the two fragments */
- /* (This is necessary for undo to work correctly) */
- IntGroupIteratorInit(ig, &iter);
- bg = NULL;
- while ((i = IntGroupIteratorNext(&iter)) >= 0) {
- Atom *ap = ATOM_AT_INDEX(mol1->atoms, i);
- Int j, *cp;
- cp = AtomConnectData(&ap->connect);
- for (j = 0; j < ap->connect.count; j++) {
- int n = cp[j];
- if (!IntGroupLookup(ig, n, NULL)) {
- /* bond i-n, i is in ig and n is not */
- int k = MoleculeLookupBond(mol1, i, n);
- if (k >= 0) {
- if (bg == NULL)
- bg = IntGroupNew();
- IntGroupAdd(bg, k, 1);
- }
- }
- }
- }
- IntGroupIteratorRelease(&iter);
- if (bg != NULL) {
- /* Remove bonds */
- MolActionCreateAndPerform(mol1, gMolActionDeleteBonds, bg);
- IntGroupRelease(bg);
- }
- /* Remove atoms */
- if (MolActionCreateAndPerform(mol1, gMolActionUnmergeMolecule, ig) == 0)
- return Qnil;
+ Molecule *mol;
+ VALUE group;
+ IntGroup *ig;
+ Vector v;
+ Data_Get_Struct(self, Molecule, mol);
+ rb_scan_args(argc, argv, "01", &group);
+ ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+ s_Molecule_DoCenterOfMass(mol, &v, ig);
+ if (ig != NULL)
+ IntGroupRelease(ig);
+ v.x = -v.x;
+ v.y = -v.y;
+ v.z = -v.z;
+ MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
return self;
}
/*
* call-seq:
- * create_atom(name, pos = -1) -> AtomRef
+ * bounds(group = nil) -> [min, max]
*
- * Create a new atom with the specified name (may contain residue
- * information) and position (if position is out of range, the atom is appended at
- * the end). Returns the reference to the new atom.
- * This operation is undoable.
+ * Calculate the boundary. The return value is an array of two Vector3D objects.
*/
static VALUE
-s_Molecule_CreateAnAtom(int argc, VALUE *argv, VALUE self)
+s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- Int i, pos;
- VALUE name, ival;
- Atom arec;
- AtomRef *aref;
- char *p, resName[6], atomName[6];
- int resSeq;
+ VALUE group;
+ IntGroup *ig;
+ Vector vmin, vmax;
+ int n;
+ Atom *ap;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "02", &name, &ival);
- if (ival != Qnil)
- pos = NUM2INT(rb_Integer(ival));
- else pos = -1;
- if (name != Qnil) {
- p = StringValuePtr(name);
- if (p[0] != 0) {
- i = MoleculeAnalyzeAtomName(p, resName, &resSeq, atomName);
- if (atomName[0] == 0)
- rb_raise(rb_eMolbyError, "bad atom name specification: %s", p);
- }
- } else p = NULL;
- if (p == NULL || p[0] == 0) {
- memset(atomName, 0, 4);
- resSeq = -1;
+ rb_scan_args(argc, argv, "01", &group);
+ ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
+ if (ig != NULL && IntGroupGetCount(ig) == 0)
+ rb_raise(rb_eMolbyError, "atom group is empty");
+ vmin.x = vmin.y = vmin.z = 1e30;
+ vmax.x = vmax.y = vmax.z = -1e30;
+ for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
+ Vector r;
+ if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
+ continue;
+ r = ap->r;
+ if (r.x < vmin.x)
+ vmin.x = r.x;
+ if (r.y < vmin.y)
+ vmin.y = r.y;
+ if (r.z < vmin.z)
+ vmin.z = r.z;
+ if (r.x > vmax.x)
+ vmax.x = r.x;
+ if (r.y > vmax.y)
+ vmax.y = r.y;
+ if (r.z > vmax.z)
+ vmax.z = r.z;
+ }
+ return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
+}
+
+/* Get atom position or a vector */
+static void
+s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
+{
+ if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
+ int n1 = s_Molecule_AtomIndexFromValue(mol, val);
+ *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
+ } else {
+ VectorFromValue(val, vp);
}
- memset(&arec, 0, sizeof(arec));
- strncpy(arec.aname, atomName, 4);
- if (resSeq >= 0) {
- strncpy(arec.resName, resName, 4);
- arec.resSeq = resSeq;
- }
- arec.occupancy = 1.0;
-// i = MoleculeCreateAnAtom(mol, &arec);
- if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
- return Qnil;
- aref = AtomRefNew(mol, pos);
- return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
}
/*
* call-seq:
- * duplicate_atom(atomref, pos = -1) -> AtomRef
+ * measure_bond(n1, n2) -> Float
*
- * Create a new atom with the same attributes (but no bonding information)
- * with the specified atom. Returns the reference to the new atom.
- * This operation is undoable.
+ * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
+ * or Vector3D values.
+ * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
*/
static VALUE
-s_Molecule_DuplicateAnAtom(int argc, VALUE *argv, VALUE self)
+s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
{
Molecule *mol;
- const Atom *apsrc;
- Atom arec;
- AtomRef *aref;
- VALUE retval, aval, ival;
- Int pos;
+ Vector v1, v2;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "11", &aval, &ival);
- if (FIXNUM_P(aval)) {
- int idx = NUM2INT(aval);
- if (idx < 0 || idx >= mol->natoms)
- rb_raise(rb_eMolbyError, "atom index out of range: %d", idx);
- apsrc = ATOM_AT_INDEX(mol->atoms, idx);
- } else {
- apsrc = s_AtomFromValue(aval);
- }
- if (apsrc == NULL)
- rb_raise(rb_eMolbyError, "bad atom specification");
- if (ival != Qnil)
- pos = NUM2INT(rb_Integer(ival));
- else pos = -1;
- AtomDuplicate(&arec, apsrc);
- arec.connect.count = 0;
- if (MolActionCreateAndPerform(mol, gMolActionAddAnAtom, &arec, pos, &pos) != 0)
- retval = Qnil;
- else {
- aref = AtomRefNew(mol, pos);
- retval = Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
- }
- AtomClean(&arec);
- return retval;
+ s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+ s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+ return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
}
/*
* call-seq:
- * create_bond(n1, n2, ...) -> Integer
+ * measure_angle(n1, n2, n3) -> Float
*
- * Create bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is already present for a particular pair,
- * do nothing for that pair. Returns the number of bonds actually created.
- * This operation is undoable.
+ * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
+ * or Vector3D values. The return value is in degree.
+ * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
*/
static VALUE
-s_Molecule_CreateBond(int argc, VALUE *argv, VALUE self)
+s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
{
Molecule *mol;
- Int i, j, k, *ip, old_nbonds;
- if (argc == 0)
- rb_raise(rb_eMolbyError, "missing arguments");
- if (argc % 2 != 0)
- rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
- Data_Get_Struct(self, Molecule, mol);
- ip = ALLOC_N(Int, argc + 1);
- for (i = j = 0; i < argc; i++, j++) {
- ip[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
- if (i % 2 == 1) {
- if (MoleculeLookupBond(mol, ip[j - 1], ip[j]) >= 0)
- j -= 2; /* This bond is already present: skip it */
- else {
- for (k = 0; k < j - 1; k += 2) {
- if ((ip[k] == ip[j - 1] && ip[k + 1] == ip[j]) || (ip[k + 1] == ip[j - 1] && ip[k] == ip[j])) {
- j -= 2; /* The same entry is already in the argument */
- break;
- }
- }
- }
- }
- }
- old_nbonds = mol->nbonds;
- if (j > 0) {
- ip[j] = kInvalidIndex;
- i = MolActionCreateAndPerform(mol, gMolActionAddBonds, j, ip, NULL);
- } else i = 0;
- xfree(ip);
- if (i == -1)
- rb_raise(rb_eMolbyError, "atom index out of range");
- else if (i == -2)
- rb_raise(rb_eMolbyError, "too many bonds");
- else if (i == -3)
- rb_raise(rb_eMolbyError, "duplicate bonds");
- else if (i == -5)
- rb_raise(rb_eMolbyError, "cannot create bond to itself");
- else if (i != 0)
- rb_raise(rb_eMolbyError, "error in creating bonds");
- return INT2NUM(mol->nbonds - old_nbonds);
-}
-
-/*
- * call-seq:
- * molecule.remove_bonds(n1, n2, ...) -> Integer
- *
- * Remove bonds between atoms n1 and n2, n3 and n4, and so on. If the corresponding bond is not present for
- * a particular pair, do nothing for that pair. Returns the number of bonds actually removed.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_RemoveBond(int argc, VALUE *argv, VALUE self)
-{
- Molecule *mol;
- Int i, j, n[2];
- IntGroup *bg;
- if (argc == 0)
- rb_raise(rb_eMolbyError, "missing arguments");
- if (argc % 2 != 0)
- rb_raise(rb_eMolbyError, "bonds should be specified by pairs of atom indices");
- Data_Get_Struct(self, Molecule, mol);
- bg = NULL;
- for (i = j = 0; i < argc; i++, j = 1 - j) {
- n[j] = s_Molecule_AtomIndexFromValue(mol, argv[i]);
- if (j == 1) {
- Int k = MoleculeLookupBond(mol, n[0], n[1]);
- if (k >= 0) {
- if (bg == NULL)
- bg = IntGroupNew();
- IntGroupAdd(bg, k, 1);
- }
- }
- }
- if (bg != NULL) {
- MolActionCreateAndPerform(mol, gMolActionDeleteBonds, bg);
- i = IntGroupGetCount(bg);
- IntGroupRelease(bg);
- } else i = 0;
- return INT2NUM(i);
-}
-
-/*
- * call-seq:
- * assign_bond_order(idx, d1)
- * assign_bond_orders(group, [d1, d2, ...])
- *
- * Assign bond order. In the first form, the bond order of the idx-th bond is set to d1 (a Float value).
- * In the second form, the bond orders at the indices in the group are set to d1, d2, etc.
- * At present, the bond orders are only used in UFF parameter guess, and not in the MM/MD calculations.
- * (This may change in the future)
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_AssignBondOrder(VALUE self, VALUE idxval, VALUE dval)
-{
- Molecule *mol;
- IntGroup *ig;
- Data_Get_Struct(self, Molecule, mol);
- if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
- /* The first form */
- Int idx = NUM2INT(rb_Integer(idxval));
- Double d1 = NUM2DBL(rb_Float(dval));
- if (idx < 0 || idx >= mol->nbonds)
- rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
- ig = IntGroupNewWithPoints(idx, 1, -1);
- MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, 1, &d1, ig);
- IntGroupRelease(ig);
- } else {
- Int i, n;
- Double *dp;
- ig = IntGroupFromValue(idxval);
- n = IntGroupGetCount(ig);
- if (n == 0)
- rb_raise(rb_eMolbyError, "the bond index is empty");
- dval = rb_ary_to_ary(dval);
- dp = (Double *)calloc(sizeof(Double), n);
- for (i = 0; i < RARRAY_LEN(dval) && i < n; i++) {
- dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(dval)[i]));
- }
- MolActionCreateAndPerform(mol, gMolActionAssignBondOrders, n, dp, ig);
- free(dp);
- IntGroupRelease(ig);
- }
- return self;
-}
-
-/*
- * call-seq:
- * get_bond_order(idx) -> Float
- * get_bond_orders(group) -> Array
- *
- * Get the bond order. In the first form, the bond order of the idx-th bond is returned.
- * In the second form, the bond orders at the indices in the group are returned as an array.
- * If no bond order information have been assigned, returns nil (the first form)
- * or an empty array (the second form).
- */
-static VALUE
-s_Molecule_GetBondOrder(VALUE self, VALUE idxval)
-{
- Molecule *mol;
- IntGroup *ig;
- Double *dp;
- VALUE retval;
- Int i, n, numericArg;
- Data_Get_Struct(self, Molecule, mol);
- if (rb_obj_is_kind_of(idxval, rb_cNumeric)) {
- /* The first form */
- Int idx = NUM2INT(rb_Integer(idxval));
- if (idx < 0 || idx >= mol->nbonds)
- rb_raise(rb_eMolbyError, "the bond index (%d) is out of bounds", idx);
- if (mol->bondOrders == NULL)
- return Qnil;
- ig = IntGroupNewWithPoints(idx, 1, -1);
- n = 1;
- numericArg = 1;
- } else {
- if (mol->bondOrders == NULL)
- return rb_ary_new();
- ig = IntGroupFromValue(idxval);
- n = IntGroupGetCount(ig);
- if (n == 0)
- rb_raise(rb_eMolbyError, "the bond index is empty");
- numericArg = 0;
- }
- dp = (Double *)calloc(sizeof(Double), n);
- MoleculeGetBondOrders(mol, dp, ig);
- if (numericArg)
- retval = rb_float_new(dp[0]);
- else {
- retval = rb_ary_new();
- for (i = 0; i < n; i++)
- rb_ary_push(retval, rb_float_new(dp[i]));
- }
- free(dp);
- IntGroupRelease(ig);
- return retval;
-}
-
-/*
- * call-seq:
- * add_angle(n1, n2, n3) -> Molecule
- *
- * Add angle n1-n2-n3. Returns self. Usually, angles are automatically added
- * when a bond is created, so it is rarely necessary to use this method explicitly.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_AddAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
-{
- Int n[4];
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- if (MoleculeLookupAngle(mol, n[0], n[1], n[2]) >= 0)
- rb_raise(rb_eMolbyError, "angle %d-%d-%d is already present", n[0], n[1], n[2]);
- n[3] = kInvalidIndex;
- MolActionCreateAndPerform(mol, gMolActionAddAngles, 3, n, NULL);
- return self;
-}
-
-/*
- * call-seq:
- * remove_angle(n1, n2, n3) -> Molecule
- *
- * Remove angle n1-n2-n3. Returns self. Usually, angles are automatically removed
- * when a bond is removed, so it is rarely necessary to use this method explicitly.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_RemoveAngle(VALUE self, VALUE v1, VALUE v2, VALUE v3)
-{
- Int n[4];
- Molecule *mol;
- IntGroup *ig;
- Data_Get_Struct(self, Molecule, mol);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- if ((n[3] = MoleculeLookupAngle(mol, n[0], n[1], n[2])) < 0)
- rb_raise(rb_eMolbyError, "angle %d-%d-%d is not present", n[0], n[1], n[2]);
- ig = IntGroupNewWithPoints(n[3], 1, -1);
- MolActionCreateAndPerform(mol, gMolActionDeleteAngles, ig);
- IntGroupRelease(ig);
- return self;
-}
-
-/*
- * call-seq:
- * add_dihedral(n1, n2, n3, n4) -> Molecule
- *
- * Add dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically added
- * when a bond is created, so it is rarely necessary to use this method explicitly.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_AddDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
-{
- Int n[5];
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
- if (MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3]) >= 0)
- rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
- n[4] = kInvalidIndex;
- MolActionCreateAndPerform(mol, gMolActionAddDihedrals, 4, n, NULL);
- return self;
-}
-
-/*
- * call-seq:
- * remove_dihedral(n1, n2, n3, n4) -> Molecule
- *
- * Remove dihedral n1-n2-n3-n4. Returns self. Usually, dihedrals are automatically removed
- * when a bond is removed, so it is rarely necessary to use this method explicitly.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_RemoveDihedral(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
-{
- Int n[5];
- Molecule *mol;
- IntGroup *ig;
- Data_Get_Struct(self, Molecule, mol);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
- if ((n[4] = MoleculeLookupDihedral(mol, n[0], n[1], n[2], n[3])) < 0)
- rb_raise(rb_eMolbyError, "dihedral %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
- ig = IntGroupNewWithPoints(n[4], 1, -1);
- MolActionCreateAndPerform(mol, gMolActionDeleteDihedrals, ig);
- IntGroupRelease(ig);
- return self;
-}
-
-/*
- * call-seq:
- * add_improper(n1, n2, n3, n4) -> Molecule
- *
- * Add dihedral n1-n2-n3-n4. Returns self. Unlike angles and dihedrals, impropers are
- * not automatically added when a new bond is created, so this method is more useful than
- * the angle/dihedral counterpart.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_AddImproper(VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4)
-{
- Int n[5];
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
- if (MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3]) >= 0)
- rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is already present", n[0], n[1], n[2], n[3]);
- n[4] = kInvalidIndex;
- MolActionCreateAndPerform(mol, gMolActionAddImpropers, 4, n, NULL);
- return self;
-}
-
-/*
- * call-seq:
- * remove_improper(n1, n2, n3, n4) -> Molecule
- * remove_improper(intgroup) -> Molecule
- *
- * Remove improper n1-n2-n3-n4, or the specified impropers (in indices) in IntGroup.
- * Returns self. Unlike angles and dihedrals, impropers are
- * not automatically added when a new bond is created, so this method is more useful than
- * the angle/dihedral counterpart.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_RemoveImproper(int argc, VALUE *argv, VALUE self)
-{
- Int n[5];
- VALUE v1, v2, v3, v4;
- Molecule *mol;
- IntGroup *ig;
- Data_Get_Struct(self, Molecule, mol);
- if (argc == 1) {
- ig = IntGroupFromValue(argv[0]);
- } else {
- rb_scan_args(argc, argv, "40", &v1, &v2, &v3, &v4);
- n[0] = s_Molecule_AtomIndexFromValue(mol, v1);
- n[1] = s_Molecule_AtomIndexFromValue(mol, v2);
- n[2] = s_Molecule_AtomIndexFromValue(mol, v3);
- n[3] = s_Molecule_AtomIndexFromValue(mol, v4);
- if ((n[4] = MoleculeLookupImproper(mol, n[0], n[1], n[2], n[3])) < 0)
- rb_raise(rb_eMolbyError, "improper %d-%d-%d-%d is not present", n[0], n[1], n[2], n[3]);
- ig = IntGroupNewWithPoints(n[4], 1, -1);
- }
- MolActionCreateAndPerform(mol, gMolActionDeleteImpropers, ig);
- IntGroupRelease(ig);
- return self;
-}
-
-/*
- * call-seq:
- * assign_residue(group, res) -> Molecule
- *
- * Assign the specified atoms as the given residue. res can either be an integer, "resname"
- * or "resname.resno". When the residue number is not specified, the residue number of
- * the first atom in the group is used.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_AssignResidue(VALUE self, VALUE range, VALUE res)
-{
- Molecule *mol;
- IntGroup *ig;
- char *p, *pp, buf[16];
- Int resid, n;
- Atom *ap;
- Data_Get_Struct(self, Molecule, mol);
-
- /* Parse the argument res */
- if (FIXNUM_P(res)) {
- /* We can assume Fixnum here because Bignum is non-realistic as residue numbers */
- resid = NUM2INT(res);
- buf[0] = 0;
- } else {
- p = StringValuePtr(res);
- pp = strchr(p, '.');
- if (pp != NULL) {
- resid = atoi(pp + 1);
- n = pp - p;
- } else {
- resid = -1;
- n = strlen(p);
- }
- if (n > sizeof buf - 1)
- n = sizeof buf - 1;
- strncpy(buf, p, n);
- buf[n] = 0;
- }
- ig = s_Molecule_AtomGroupFromValue(self, range);
- if (ig == NULL || IntGroupGetCount(ig) == 0)
- return Qnil;
-
- if (resid < 0) {
- /* Use the residue number of the first specified atom */
- n = IntGroupGetNthPoint(ig, 0);
- if (n >= mol->natoms)
- rb_raise(rb_eMolbyError, "Atom index (%d) out of range", n);
- ap = ATOM_AT_INDEX(mol->atoms, n);
- resid = ap->resSeq;
- }
- /* Change the residue number */
- MolActionCreateAndPerform(mol, gMolActionChangeResidueNumber, ig, resid);
- /* Change the residue name if necessary */
- if (buf[0] != 0) {
- /* Int seqs[2];
- seqs[0] = resid;
- seqs[1] = kInvalidIndex; */
- MolActionCreateAndPerform(mol, gMolActionChangeResidueNames, 1, &resid, 4, buf);
- }
- IntGroupRelease(ig);
- return self;
-}
-
-/*
- * call-seq:
- * offset_residue(group, offset) -> Molecule
- *
- * Offset the residue number of the specified atoms. If any of the residue number gets
- * negative, then exception is thrown.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_OffsetResidue(VALUE self, VALUE range, VALUE offset)
-{
- Molecule *mol;
- IntGroup *ig;
- int ofs, result;
- Data_Get_Struct(self, Molecule, mol);
- ig = s_Molecule_AtomGroupFromValue(self, range);
- ofs = NUM2INT(offset);
- result = MolActionCreateAndPerform(mol, gMolActionOffsetResidueNumbers, ig, ofs, -1);
- if (result > 0)
- rb_raise(rb_eMolbyError, "residue number of atom %d becomes negative", result - 1);
- IntGroupRelease(ig);
- return self;
-}
-
-/*
- * call-seq:
- * renumber_atoms(array) -> IntGroup
- *
- * Change the order of atoms so that the atoms specified in the array argument appear
- * in this order from the top of the molecule. The atoms that are not included in array
- * are placed after these atoms, and these atoms are returned as an intGroup.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_RenumberAtoms(VALUE self, VALUE array)
-{
- Molecule *mol;
- Int *new2old;
- IntGroup *ig;
- int i, n;
- VALUE *valp, retval;
- Data_Get_Struct(self, Molecule, mol);
- if (TYPE(array) != T_ARRAY)
- array = rb_funcall(array, rb_intern("to_a"), 0);
- n = RARRAY_LEN(array);
- valp = RARRAY_PTR(array);
- new2old = ALLOC_N(Int, n + 1);
- for (i = 0; i < n; i++)
- new2old[i] = s_Molecule_AtomIndexFromValue(mol, valp[i]);
- new2old[i] = kInvalidIndex;
- i = MolActionCreateAndPerform(mol, gMolActionRenumberAtoms, i, new2old);
- if (i == 1)
- rb_raise(rb_eMolbyError, "Atom index out of range");
- else if (i == 2)
- rb_raise(rb_eMolbyError, "Duplicate entry");
- else if (i == 3)
- rb_raise(rb_eMolbyError, "Internal inconsistency during atom renumbering");
- retval = IntGroup_Alloc(rb_cIntGroup);
- Data_Get_Struct(retval, IntGroup, ig);
- if (mol->natoms > n)
- IntGroup_RaiseIfError(IntGroupAdd(ig, n, mol->natoms - n));
- xfree(new2old);
- return retval;
+ Vector v1, v2, v3;
+ Double d;
+ Data_Get_Struct(self, Molecule, mol);
+ s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+ s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+ s_Molecule_GetVectorFromArg(mol, nval3, &v3);
+ d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
+ if (isnan(d))
+ return Qnil; /* Cannot define */
+ else return rb_float_new(d);
}
/*
* call-seq:
- * find_close_atoms(atom, limit = 1.2) -> array of Integers (atom indices)
+ * measure_dihedral(n1, n2, n3, n4) -> Float
*
- * Find atoms that are within the threshold distance from the given atom.
- * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
- * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
- * If limit is not given, a default value of 1.2 is used.
- * An array of atom indices is returned. If no atoms are found, an empty array is returned.
+ * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
+ * or Vector3D values. The return value is in degree.
+ * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
*/
static VALUE
-s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
+s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
{
Molecule *mol;
- VALUE aval, limval;
- double limit;
- Int n1, nbonds, *bonds;
+ Vector v1, v2, v3, v4;
+ Double d;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "11", &aval, &limval);
- n1 = s_Molecule_AtomIndexFromValue(mol, aval);
- if (limval == Qnil)
- limit = 1.2;
- else
- limit = NUM2DBL(rb_Float(limval));
- nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
- bonds = NULL;
- MoleculeFindCloseAtoms(mol, n1, limit, &nbonds, &bonds, 0);
- aval = rb_ary_new();
- if (nbonds > 0) {
- for (n1 = 0; n1 < nbonds; n1++)
- rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
- free(bonds);
- }
- return aval;
+ s_Molecule_GetVectorFromArg(mol, nval1, &v1);
+ s_Molecule_GetVectorFromArg(mol, nval2, &v2);
+ s_Molecule_GetVectorFromArg(mol, nval3, &v3);
+ s_Molecule_GetVectorFromArg(mol, nval4, &v4);
+ d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
+ if (isnan(d))
+ return Qnil; /* Cannot define */
+ else return rb_float_new(d);
}
/*
* call-seq:
- * guess_bonds(limit = 1.2) -> Integer
+ * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
*
- * Create bonds between atoms that are within the threshold distance.
- * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
- * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
- * If limit is not given, a default value of 1.2 is used.
- * The number of the newly created bonds is returned.
- * This operation is undoable.
+ * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
+ * first and second atom in the pair should belong to group1 and group2, respectively.
+ * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
*/
static VALUE
-s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
+s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE limval;
- double limit;
- Int nbonds, *bonds;
+ VALUE limval, gval1, gval2, rval, igval;
+ IntGroup *ig1, *ig2;
+ IntGroupIterator iter1, iter2;
+ Int npairs, *pairs;
+ Int n[2], i;
+ Double lim;
+ Vector r1;
+ Atom *ap1, *ap2;
+ MDExclusion *exinfo;
+ Int *exlist;
+
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &limval);
- if (limval == Qnil)
- limit = 1.2;
+ rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
+ lim = NUM2DBL(rb_Float(limval));
+ if (lim <= 0.0)
+ rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
+ if (gval1 != Qnil)
+ ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
else
- limit = NUM2DBL(rb_Float(limval));
- MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
- if (nbonds > 0) {
- MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
- free(bonds);
- }
- return INT2NUM(nbonds);
-}
+ ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
+ if (gval2 != Qnil)
+ ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
+ else
+ ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
-/*
- * call-seq:
- * register_undo(script, *args)
- *
- * Register an undo operation with the current molecule.
- */
-static VALUE
-s_Molecule_RegisterUndo(int argc, VALUE *argv, VALUE self)
-{
- Molecule *mol;
- VALUE script, args;
- MolAction *act;
- Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "1*", &script, &args);
- act = MolActionNew(SCRIPT_ACTION("R"), StringValuePtr(script), args);
- MolActionCallback_registerUndo(mol, act);
- return script;
-}
-
-/*
- * call-seq:
- * undo_enabled? -> bool
- *
- * Returns true if undo is enabled for this molecule; otherwise no.
- */
-static VALUE
-s_Molecule_UndoEnabled(VALUE self)
-{
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- if (MolActionCallback_isUndoRegistrationEnabled(mol))
- return Qtrue;
- else return Qfalse;
-}
-
-/*
- * call-seq:
- * undo_enabled = bool
- *
- * Enable or disable undo.
- */
-static VALUE
-s_Molecule_SetUndoEnabled(VALUE self, VALUE val)
-{
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- MolActionCallback_setUndoRegistrationEnabled(mol, (val != Qfalse && val != Qnil));
- return val;
-}
-
-/*
- * call-seq:
- * selection -> IntGroup
- *
- * Returns the current selection.
- */
-static VALUE
-s_Molecule_Selection(VALUE self)
-{
- Molecule *mol;
- IntGroup *ig;
- VALUE val;
- Data_Get_Struct(self, Molecule, mol);
- if (mol != NULL && (ig = MoleculeGetSelection(mol)) != NULL) {
- ig = IntGroupNewFromIntGroup(ig); /* Duplicate, so that the change from GUI does not affect the value */
- val = ValueFromIntGroup(ig);
- IntGroupRelease(ig);
+ if (!RTEST(igval)) {
+ /* Use the exclusion table in MDArena */
+ if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
+ VALUE mval = ValueFromMolecule(mol);
+ s_RebuildMDParameterIfNecessary(mval, Qnil);
+ }
+ exinfo = mol->arena->exinfo; /* May be NULL */
+ exlist = mol->arena->exlist;
} else {
- val = IntGroup_Alloc(rb_cIntGroup);
+ exinfo = NULL;
+ exlist = NULL;
}
- return val;
-}
-
-static VALUE
-s_Molecule_SetSelectionSub(VALUE self, VALUE val, int undoable)
-{
- Molecule *mol;
- IntGroup *ig;
- Data_Get_Struct(self, Molecule, mol);
- if (val == Qnil)
- ig = NULL;
- else
- ig = s_Molecule_AtomGroupFromValue(self, val);
- if (undoable)
- MolActionCreateAndPerform(mol, gMolActionSetSelection, ig);
- else
- MoleculeSetSelection(mol, ig);
- if (ig != NULL)
- IntGroupRelease(ig);
- return val;
-}
-
-/*
- * call-seq:
- * selection = IntGroup
- *
- * Set the current selection. The right-hand operand may be nil.
- * This operation is _not_ undoable. If you need undo, use set_undoable_selection instead.
- */
-static VALUE
-s_Molecule_SetSelection(VALUE self, VALUE val)
-{
- return s_Molecule_SetSelectionSub(self, val, 0);
-}
-
-/*
- * call-seq:
- * set_undoable_selection(IntGroup)
- *
- * Set the current selection with undo registration. The right-hand operand may be nil.
- * This operation is undoable.
- */
-static VALUE
-s_Molecule_SetUndoableSelection(VALUE self, VALUE val)
-{
- return s_Molecule_SetSelectionSub(self, val, 1);
+ IntGroupIteratorInit(ig1, &iter1);
+ IntGroupIteratorInit(ig2, &iter2);
+ npairs = 0;
+ pairs = NULL;
+ while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
+ Int exn1, exn2;
+ ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
+ r1 = ap1->r;
+ if (exinfo != NULL) {
+ exn1 = exinfo[n[0]].index1;
+ exn2 = exinfo[n[0] + 1].index1;
+ } else exn1 = exn2 = -1;
+ IntGroupIteratorReset(&iter2);
+ while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
+ ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
+ if (n[0] == n[1])
+ continue; /* Same atom */
+ if (exinfo != NULL) {
+ /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
+ for (i = exn1; i < exn2; i++) {
+ if (exlist[i] == n[1])
+ break;
+ }
+ if (i < exn2)
+ continue; /* Should be excluded */
+ }
+ if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
+ /* Is this pair already registered? */
+ Int *ip;
+ for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
+ if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
+ break;
+ }
+ if (i >= npairs) {
+ /* Not registered yet */
+ AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
+ }
+ }
+ }
+ }
+ IntGroupIteratorRelease(&iter2);
+ IntGroupIteratorRelease(&iter1);
+ IntGroupRelease(ig2);
+ IntGroupRelease(ig1);
+ rval = rb_ary_new2(npairs);
+ if (pairs != NULL) {
+ for (i = 0; i < npairs; i++) {
+ rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
+ }
+ free(pairs);
+ }
+ return rval;
}
/*
* call-seq:
- * hidden_atoms -> IntGroup
+ * find_close_atoms(atom, limit = 1.2, radius = 0.77) -> array of Integers (atom indices)
*
- * Returns the currently hidden atoms.
+ * Find atoms that are within the threshold distance from the given atom.
+ * (The atom argument can also be a vector, representing a cartesian coordinate. In that case, the van der Waals of the atom can also be specified.)
+ * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
+ * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
+ * If limit is not given, a default value of 1.2 is used.
+ * An array of atom indices is returned. If no atoms are found, an empty array is returned.
*/
static VALUE
-s_Molecule_HiddenAtoms(VALUE self)
+s_Molecule_FindCloseAtoms(int argc, VALUE *argv, VALUE self)
{
- rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
- return Qnil; /* Not reached */
-/* Molecule *mol;
- IntGroup *ig;
- VALUE val;
+ Molecule *mol;
+ VALUE aval, limval, radval;
+ double limit, radius;
+ Int n1, nbonds, *bonds, an;
+ Vector v;
Data_Get_Struct(self, Molecule, mol);
- if (mol != NULL) {
- Atom *ap;
- int i;
- ig = IntGroupNew();
- for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
- if (ap->exflags & kAtomHiddenFlag)
- IntGroupAdd(ig, i, 1);
- }
- val = ValueFromIntGroup(ig);
- IntGroupRelease(ig);
- rb_obj_freeze(val);
- return val;
- } else return Qnil; */
+ rb_scan_args(argc, argv, "12", &aval, &limval, &radval);
+ if (rb_obj_is_kind_of(aval, rb_cVector3D) || rb_obj_is_kind_of(aval, rb_cLAMatrix) || rb_obj_is_kind_of(aval, rb_mEnumerable)) {
+ VectorFromValue(aval, &v);
+ if (radval == Qnil)
+ radius = gElementParameters[6].radius;
+ else
+ radius = NUM2DBL(rb_Float(radval));
+ n1 = mol->natoms;
+ } else {
+ n1 = s_Molecule_AtomIndexFromValue(mol, aval);
+ v = ATOM_AT_INDEX(mol->atoms, n1)->r;
+ an = ATOM_AT_INDEX(mol->atoms, n1)->atomicNumber;
+ if (an >= 0 && an < gCountElementParameters)
+ radius = gElementParameters[an].radius;
+ else radius = gElementParameters[6].radius;
+ }
+ if (limval == Qnil)
+ limit = 1.2;
+ else
+ limit = NUM2DBL(rb_Float(limval));
+ nbonds = 0; /* This initialization is necessary: see comments in MoleculeFindCloseAtoms() */
+ bonds = NULL;
+ MoleculeFindCloseAtoms(mol, &v, radius, limit, &nbonds, &bonds, n1);
+ aval = rb_ary_new();
+ if (nbonds > 0) {
+ for (n1 = 0; n1 < nbonds; n1++)
+ rb_ary_push(aval, INT2NUM(bonds[n1 * 2 + 1]));
+ free(bonds);
+ }
+ return aval;
}
/*
* call-seq:
- * set_hidden_atoms(IntGroup)
- * self.hidden_atoms = IntGroup
+ * guess_bonds(limit = 1.2) -> Integer
*
- * Hide the specified atoms. This operation is _not_ undoable.
+ * Create bonds between atoms that are within the threshold distance.
+ * If limit is a positive number, the threshold distance is the sum of the vdw radii times limit.
+ * If limit is a negative number, its absolute value is used for the threshold distance in angstrom.
+ * If limit is not given, a default value of 1.2 is used.
+ * The number of the newly created bonds is returned.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
+s_Molecule_GuessBonds(int argc, VALUE *argv, VALUE self)
{
- rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
- return Qnil; /* Not reached */
-/*
- Molecule *mol;
+ Molecule *mol;
+ VALUE limval;
+ double limit;
+ Int nbonds, *bonds;
Data_Get_Struct(self, Molecule, mol);
- if (mol != NULL) {
- Atom *ap;
- int i;
- IntGroup *ig;
- if (val == Qnil)
- ig = NULL;
- else
- ig = s_Molecule_AtomGroupFromValue(self, val);
- for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
- if (ig != NULL && IntGroupLookup(ig, i, NULL)) {
- ap->exflags |= kAtomHiddenFlag;
- } else {
- ap->exflags &= kAtomHiddenFlag;
- }
- }
- if (ig != NULL)
- IntGroupRelease(ig);
- MoleculeCallback_notifyModification(mol, 0);
+ rb_scan_args(argc, argv, "01", &limval);
+ if (limval == Qnil)
+ limit = 1.2;
+ else
+ limit = NUM2DBL(rb_Float(limval));
+ MoleculeGuessBonds(mol, limit, &nbonds, &bonds);
+ if (nbonds > 0) {
+ MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
+ free(bonds);
}
- return val; */
+ return INT2NUM(nbonds);
}
+#pragma mark ------ Cell and Symmetry ------
+
/*
* call-seq:
- * select_frame(index)
- * frame = index
+ * cell -> [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
*
- * Select the specified frame. If successful, returns true, otherwise returns false.
+ * Returns the unit cell parameters. If cell is not set, returns nil.
*/
static VALUE
-s_Molecule_SelectFrame(VALUE self, VALUE val)
+s_Molecule_Cell(VALUE self)
{
Molecule *mol;
- int ival = NUM2INT(val);
+ int i;
+ VALUE val;
Data_Get_Struct(self, Molecule, mol);
- ival = MoleculeSelectFrame(mol, ival, 1);
- if (ival >= 0)
- return Qtrue;
- else return Qfalse;
+ if (mol->cell == NULL)
+ return Qnil;
+ val = rb_ary_new2(6);
+ for (i = 0; i < 6; i++)
+ rb_ary_push(val, rb_float_new(mol->cell->cell[i]));
+ if (mol->cell->has_sigma) {
+ for (i = 0; i < 6; i++) {
+ rb_ary_push(val, rb_float_new(mol->cell->cellsigma[i]));
+ }
+ }
+ return val;
}
/*
* call-seq:
- * frame -> Integer
+ * cell = [a, b, c, alpha, beta, gamma [, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]]
+ * set_cell([a, b, c, alpha, beta, gamma[, sig_a, sig_b, sig_c, sig_alpha, sig_beta, sig_gamma]], convert_coord = nil)
*
- * Get the current frame.
+ * Set the unit cell parameters. If the cell value is nil, then clear the current cell.
+ If the given argument has 12 or more members, then the second half of the parameters represents the sigma values.
+ This operation is undoable.
+ Convert_coord is a flag to specify that the coordinates should be transformed so that the fractional coordinates remain the same.
*/
static VALUE
-s_Molecule_Frame(VALUE self)
+s_Molecule_SetCell(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
+ VALUE val, cval;
+ int i, convert_coord, n;
+ double d[12];
Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(mol->cframe);
+ rb_scan_args(argc, argv, "11", &val, &cval);
+ if (val == Qnil) {
+ n = 0;
+ } else {
+ int len;
+ val = rb_ary_to_ary(val);
+ len = RARRAY_LEN(val);
+ if (len >= 12) {
+ n = 12;
+ } else if (len >= 6) {
+ n = 6;
+ } else rb_raise(rb_eMolbyError, "too few members for cell parameters (6 or 12 required)");
+ for (i = 0; i < n; i++)
+ d[i] = NUM2DBL(rb_Float((RARRAY_PTR(val))[i]));
+ }
+ convert_coord = (RTEST(cval) ? 1 : 0);
+ MolActionCreateAndPerform(mol, gMolActionSetCell, n, d, convert_coord);
+ return val;
}
/*
* call-seq:
- * nframes -> Integer
+ * box -> [avec, bvec, cvec, origin, flags]
*
- * Get the number of frames.
+ * Get the unit cell information in the form of a periodic bounding box.
+ * Avec, bvec, cvec, origin are Vector3D objects, and flags is a 3-member array of
+ * Integers which define whether the system is periodic along the axis.
+ * If no unit cell is defined, nil is returned.
*/
static VALUE
-s_Molecule_Nframes(VALUE self)
+s_Molecule_Box(VALUE self)
{
Molecule *mol;
+ VALUE v[5], val;
Data_Get_Struct(self, Molecule, mol);
- return INT2NUM(MoleculeGetNumberOfFrames(mol));
+ if (mol == NULL || mol->cell == NULL)
+ return Qnil;
+ v[0] = ValueFromVector(&(mol->cell->axes[0]));
+ v[1] = ValueFromVector(&(mol->cell->axes[1]));
+ v[2] = ValueFromVector(&(mol->cell->axes[2]));
+ v[3] = ValueFromVector(&(mol->cell->origin));
+ v[4] = rb_ary_new3(3, INT2NUM(mol->cell->flags[0]), INT2NUM(mol->cell->flags[1]), INT2NUM(mol->cell->flags[2]));
+ val = rb_ary_new4(5, v);
+ return val;
}
/*
* call-seq:
- * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
- * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
+ * set_box(avec, bvec, cvec, origin = [0, 0, 0], flags = [1, 1, 1], convert_coordinates = nil)
+ * set_box(d, origin = [0, 0, 0])
+ * set_box
*
- * Insert new frames at the indices specified by the intGroup. If the first argument is
- * an integer, a single new frame is inserted at that index. If the first argument is
- * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
- * should be an array of arrays of Vector3Ds, then those coordinates are set
- * to the new frame. Otherwise, the coordinates of current molecule are copied
- * to the new frame.
- * Returns an intGroup representing the inserted frames if successful, nil if not.
+ * Set the unit cell parameters. Avec, bvec, and cvec can be either a Vector3D or a number.
+ If it is a number, the x/y/z axis vector is multiplied with the given number and used
+ as the box vector.
+ Flags, if present, is a 3-member array of Integers defining whether the system is
+ periodic along the axis.
+ If convert_coordinates is true, then the coordinates are converted so that the fractional coordinates remain the same.
+ In the second form, an isotropic box with cell-length d is set.
+ In the third form, the existing box is cleared.
+ Note: the sigma of the cell parameters is not cleared unless the periodic box itself is cleared.
*/
static VALUE
-s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
+s_Molecule_SetBox(VALUE self, VALUE aval)
{
- VALUE val, coords, cells;
Molecule *mol;
- IntGroup *ig;
- int count, ival, i, j, len, len_c, len2, nframes;
- VALUE *ptr, *ptr2;
- Vector *vp, *vp2;
+ VALUE v[6];
+ static Vector ax[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
+ Vector vv[3];
+ Vector origin = {0, 0, 0};
+ char flags[3];
+ Double d;
+ int i, convertCoordinates = 0;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "12", &val, &coords, &cells);
- if (coords != Qnil) {
- if (TYPE(coords) != T_ARRAY)
- rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
- len = RARRAY_LEN(coords);
- } else len = 0;
- if (cells != Qnil) {
- if (mol->cell == NULL)
- rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
- if (TYPE(cells) != T_ARRAY)
- rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
- len_c = RARRAY_LEN(cells);
- } else len_c = 0;
- count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
- nframes = MoleculeGetNumberOfFrames(mol);
- if (val == Qnil) {
- ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
- val = ValueFromIntGroup(ig);
- } else {
- ig = IntGroupFromValue(val);
- }
- count = IntGroupGetCount(ig); /* Count is updated here */
- vp = ALLOC_N(Vector, mol->natoms * count);
- if (cells != Qnil)
- vp2 = ALLOC_N(Vector, 4 * count);
- else vp2 = NULL;
- if (len > 0) {
- if (len < count)
- rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
- ptr = RARRAY_PTR(coords);
- for (i = 0; i < count; i++) {
- if (TYPE(ptr[i]) != T_ARRAY)
- rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
- len2 = RARRAY_LEN(ptr[i]);
- if (len2 < mol->natoms)
- rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
- ptr2 = RARRAY_PTR(ptr[i]);
- for (j = 0; j < mol->natoms; j++)
- VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
- }
+ if (aval == Qnil) {
+ MolActionCreateAndPerform(mol, gMolActionClearBox);
+ return self;
+ }
+ aval = rb_ary_to_ary(aval);
+ for (i = 0; i < 6; i++) {
+ if (i < RARRAY_LEN(aval))
+ v[i] = (RARRAY_PTR(aval))[i];
+ else v[i] = Qnil;
+ }
+ if (v[0] == Qnil) {
+ MolActionCreateAndPerform(mol, gMolActionClearBox);
+ return self;
+ }
+ if ((v[1] == Qnil || v[2] == Qnil) && rb_obj_is_kind_of(v[0], rb_cNumeric)) {
+ d = NUM2DBL(rb_Float(v[0]));
+ for (i = 0; i < 3; i++)
+ VecScale(vv[i], ax[i], d);
+ if (v[1] != Qnil)
+ VectorFromValue(v[1], &origin);
+ flags[0] = flags[1] = flags[2] = 1;
} else {
- Atom *ap;
- for (i = 0; i < count; i++) {
- for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
- vp[i * mol->natoms + j] = ap->r;
+ for (i = 0; i < 3; i++) {
+ if (v[i] == Qnil) {
+ VecZero(vv[i]);
+ } else if (rb_obj_is_kind_of(v[i], rb_cNumeric)) {
+ d = NUM2DBL(rb_Float(v[i]));
+ VecScale(vv[i], ax[i], d);
+ } else {
+ VectorFromValue(v[i], &vv[i]);
}
+ flags[i] = (VecLength2(vv[i]) > 0.0);
}
- }
- if (len_c > 0) {
- if (len_c < count)
- rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
- ptr = RARRAY_PTR(cells);
- for (i = 0; i < count; i++) {
- if (TYPE(ptr[i]) != T_ARRAY)
- rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
- len2 = RARRAY_LEN(ptr[i]);
- if (len2 < 4)
- rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
- ptr2 = RARRAY_PTR(ptr[i]);
- for (j = 0; j < 4; j++)
- VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
+ if (v[3] != Qnil)
+ VectorFromValue(v[3], &origin);
+ if (v[4] != Qnil) {
+ for (i = 0; i < 3; i++) {
+ VALUE val = Ruby_ObjectAtIndex(v[4], i);
+ flags[i] = (NUM2INT(rb_Integer(val)) != 0);
+ }
}
+ if (RTEST(v[5]))
+ convertCoordinates = 1;
}
- ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
- IntGroupRelease(ig);
- xfree(vp);
- if (vp2 != NULL)
- xfree(vp2);
- return (ival >= 0 ? val : Qnil);
+ MolActionCreateAndPerform(mol, gMolActionSetBox, &(vv[0]), &(vv[1]), &(vv[2]), &origin, (flags[0] * 4 + flags[1] * 2 + flags[2]), convertCoordinates);
+ return self;
}
/*
* call-seq:
- * create_frame(coordinates = nil) -> Integer
- * create_frames(coordinates = nil) -> Integer
+ * cell_periodicity -> [n1, n2, n3]
*
- * Same as molecule.insert_frames(nil, coordinates).
+ * Get flags denoting whether the cell is periodic along the a/b/c axes. If the cell is not defined
+ * nil is returned.
*/
static VALUE
-s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
+s_Molecule_CellPeriodicity(VALUE self)
{
- VALUE vals[3];
- rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
- vals[0] = Qnil;
- return s_Molecule_InsertFrames(3, vals, self);
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->cell == NULL)
+ return Qnil;
+ return rb_ary_new3(3, INT2FIX((int)mol->cell->flags[0]), INT2FIX((int)mol->cell->flags[1]), INT2FIX((int)mol->cell->flags[2]));
}
/*
* call-seq:
- * remove_frames(IntGroup, wantCoordinates = false)
+ * self.cell_periodicity = [n1, n2, n3] or Integer or nil
+ * set_cell_periodicity = [n1, n2, n3] or Integer or nil
*
- * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
- * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
- * removed frames is returned if operation is successful.
+ * Set whether the cell is periodic along the a/b/c axes. If an integer is given as an argument,
+ * its bits 2/1/0 (from the lowest) correspond to the a/b/c axes. Nil is equivalent to [0, 0, 0].
+ * If cell is not defined, exception is raised.
+ * This operation is undoable.
*/
static VALUE
-s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
+s_Molecule_SetCellPeriodicity(VALUE self, VALUE arg)
{
- VALUE val, flag;
- VALUE retval;
Molecule *mol;
- IntGroup *ig;
- int count;
+ Int flag;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "11", &val, &flag);
- ig = IntGroupFromValue(val);
- count = IntGroupGetCount(ig);
- if (RTEST(flag)) {
- /* Create return value before removing frames */
- VALUE coords;
- int i, j, n;
- Atom *ap;
- Vector v;
- retval = rb_ary_new2(count);
- for (i = 0; i < count; i++) {
- n = IntGroupGetNthPoint(ig, i);
- coords = rb_ary_new2(mol->natoms);
- for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
- if (n < ap->nframes && n != mol->cframe)
- v = ap->frames[n];
- else v = ap->r;
- rb_ary_push(coords, ValueFromVector(&v));
- }
- rb_ary_push(retval, coords);
+ if (mol->cell == NULL)
+ rb_raise(rb_eMolbyError, "periodic cell is not defined");
+ if (arg == Qnil)
+ flag = 0;
+ else if (rb_obj_is_kind_of(arg, rb_cNumeric))
+ flag = NUM2INT(rb_Integer(arg));
+ else {
+ Int i;
+ VALUE arg0;
+ arg = rb_ary_to_ary(arg);
+ flag = 0;
+ for (i = 0; i < 3 && i < RARRAY_LEN(arg); i++) {
+ arg0 = RARRAY_PTR(arg)[i];
+ if (arg0 != Qnil && arg0 != Qfalse && arg0 != INT2FIX(0))
+ flag |= (1 << (2 - i));
}
- } else retval = Qtrue;
- if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
- return retval;
- else return Qnil;
+ }
+ MolActionCreateAndPerform(mol, gMolActionSetCellPeriodicity, flag);
+ return arg;
}
/*
* call-seq:
- * each_frame {|n| ...}
+ * cell_flexibility -> bool
*
- * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
- * the frame number. After completion, the original frame number is restored.
+ * Returns the unit cell is flexible or not
*/
static VALUE
-s_Molecule_EachFrame(VALUE self)
+s_Molecule_CellFlexibility(VALUE self)
{
- int i, cframe, nframes;
- Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- cframe = mol->cframe;
- nframes = MoleculeGetNumberOfFrames(mol);
- if (nframes > 0) {
- for (i = 0; i < nframes; i++) {
- MoleculeSelectFrame(mol, i, 1);
- rb_yield(INT2NUM(i));
- }
- MoleculeSelectFrame(mol, cframe, 1);
- }
- return self;
+ rb_warn("cell_flexibility is obsolete (unit cell is always frame dependent)");
+ return Qtrue;
+ /* Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->cell == NULL)
+ return Qfalse;
+ if (mol->useFlexibleCell)
+ return Qtrue;
+ else return Qfalse; */
}
/*
* call-seq:
- * set_atom_attr(index, key, value)
+ * self.cell_flexibility = bool
+ * set_cell_flexibility(bool)
*
- * Set the atom attribute for the specified atom.
- * This operation is undoable.
+ * Change the unit cell is flexible or not
*/
static VALUE
-s_Molecule_SetAtomAttr(VALUE self, VALUE idx, VALUE key, VALUE val)
+s_Molecule_SetCellFlexibility(VALUE self, VALUE arg)
{
- Molecule *mol;
- VALUE aref, oldval;
- Data_Get_Struct(self, Molecule, mol);
- aref = ValueFromMoleculeAndIndex(mol, s_Molecule_AtomIndexFromValue(mol, idx));
- oldval = s_AtomRef_GetAttr(aref, key);
- if (val == Qundef)
- return oldval;
- s_AtomRef_SetAttr(aref, key, val);
- return val;
+ rb_warn("set_cell_flexibility is obsolete (unit cell is always frame dependent)");
+ return self;
+ /* Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ MolActionCreateAndPerform(mol, gMolActionSetCellFlexibility, RTEST(arg) != 0);
+ return self; */
}
/*
* call-seq:
- * get_atom_attr(index, key)
+ * cell_transform -> Transform
*
- * Get the atom attribute for the specified atom.
+ * Get the transform matrix that converts internal coordinates to cartesian coordinates.
+ * If cell is not defined, nil is returned.
*/
static VALUE
-s_Molecule_GetAtomAttr(VALUE self, VALUE idx, VALUE key)
+s_Molecule_CellTransform(VALUE self)
{
- return s_Molecule_SetAtomAttr(self, idx, key, Qundef);
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol == NULL || mol->cell == NULL)
+ return Qnil;
+ return ValueFromTransform(&(mol->cell->tr));
}
/*
* call-seq:
- * get_coord_from_frame(index, group = nil)
+ * symmetry -> Array of Transforms
+ * symmetries -> Array of Transforms
*
- * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
- * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
- * copied; now they are always copied)
+ * Get the currently defined symmetry operations. If no symmetry operation is defined,
+ * returns an empty array.
*/
static VALUE
-s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
+s_Molecule_Symmetry(VALUE self)
{
- Molecule *mol;
- VALUE ival, gval, cval;
- Int index, i, j, n, nn;
- IntGroup *ig;
- IntGroupIterator iter;
- Atom *ap;
- Vector *vp;
+ Molecule *mol;
+ VALUE val;
+ int i;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
- if (argc == 3)
- rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
- index = NUM2INT(rb_Integer(ival));
- if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
- if (n == 0)
- rb_raise(rb_eMolbyError, "No frame is present");
- else
- rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
- }
- if (gval == Qnil) {
- ig = IntGroupNewWithPoints(0, mol->natoms, -1);
- } else {
- ig = s_Molecule_AtomGroupFromValue(self, gval);
- }
- n = IntGroupGetCount(ig);
- if (n > 0) {
- vp = (Vector *)calloc(sizeof(Vector), n);
- IntGroupIteratorInit(ig, &iter);
- j = 0;
- nn = 0;
- while ((i = IntGroupIteratorNext(&iter)) >= 0) {
- ap = ATOM_AT_INDEX(mol->atoms, i);
- if (index < ap->nframes) {
- vp[j] = ap->frames[index];
- nn++;
- } else {
- vp[j] = ap->r;
- }
- j++;
- }
- if (nn > 0)
- MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
- free(vp);
- if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
- vp = mol->frame_cells + index * 4;
- MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
- }
- IntGroupIteratorRelease(&iter);
+ if (mol->nsyms <= 0)
+ return rb_ary_new();
+ val = rb_ary_new2(mol->nsyms);
+ for (i = 0; i < mol->nsyms; i++) {
+ rb_ary_push(val, ValueFromTransform(&mol->syms[i]));
}
- IntGroupRelease(ig);
- return self;
+ return val;
}
/*
* call-seq:
- * fragment(n1, *exatoms) -> IntGroup
- * fragment(group, *exatoms) -> IntGroup
+ * nsymmetries -> Integer
*
- * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
- * those atoms will not be counted during the search.
+ * Get the number of currently defined symmetry operations.
*/
static VALUE
-s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
+s_Molecule_Nsymmetries(VALUE self)
{
Molecule *mol;
- IntGroup *baseg, *ig, *exatoms;
- int n;
- volatile VALUE nval, exval;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "1*", &nval, &exval);
- if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
- baseg = NULL;
- n = NUM2INT(s_Molecule_AtomIndex(self, nval));
- } else {
- baseg = s_Molecule_AtomGroupFromValue(self, nval);
- }
- if (RARRAY_LEN(exval) == 0) {
- exatoms = NULL;
- } else {
- exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
- Data_Get_Struct(exval, IntGroup, exatoms);
- }
- if (baseg == NULL) {
- ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
- } else {
- IntGroupIterator iter;
- IntGroupIteratorInit(baseg, &iter);
- if ((n = IntGroupIteratorNext(&iter)) < 0) {
- ig = IntGroupNew();
- } else {
- ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
- if (ig != NULL) {
- while ((n = IntGroupIteratorNext(&iter)) >= 0) {
- IntGroup *subg;
- subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
- if (subg != NULL) {
- IntGroupAddIntGroup(ig, subg);
- IntGroupRelease(subg);
- }
- }
- }
- }
- IntGroupIteratorRelease(&iter);
- IntGroupRelease(baseg);
- }
- if (ig == NULL)
- rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
- nval = ValueFromIntGroup(ig);
- IntGroupRelease(ig);
- return nval;
+ return INT2NUM(mol->nsyms);
}
/*
* call-seq:
- * fragments(exclude = nil)
+ * add_symmetry(Transform) -> Integer
*
- * Returns the fragments as an array of IntGroups.
- * If exclude is given (as an array or an IntGroup), then those atoms are excluded
- * in defining the fragment.
+ * Add a new symmetry operation. If no symmetry operation is defined and the
+ * given argument is not an identity transform, then also add an identity
+ * transform at the index 0.
+ * Returns the total number of symmetries after operation.
*/
static VALUE
-s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
+s_Molecule_AddSymmetry(VALUE self, VALUE trans)
{
Molecule *mol;
- IntGroup *ag, *fg, *eg;
- VALUE gval, exval, retval;
+ Transform tr;
Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL)
- return Qnil;
- if (mol->natoms == 0)
- return rb_ary_new();
- rb_scan_args(argc, argv, "01", &exval);
- if (exval == Qnil)
- eg = NULL;
- else
- eg = IntGroupFromValue(exval);
- ag = IntGroupNewWithPoints(0, mol->natoms, -1);
- if (eg != NULL)
- IntGroupRemoveIntGroup(ag, eg);
- retval = rb_ary_new();
- while (IntGroupGetCount(ag) > 0) {
- int n = IntGroupGetNthPoint(ag, 0);
- fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
- if (fg == NULL)
- rb_raise(rb_eMolbyError, "internal error during each_fragment");
- gval = ValueFromIntGroup(fg);
- rb_ary_push(retval, gval);
- IntGroupRemoveIntGroup(ag, fg);
- IntGroupRelease(fg);
- }
- IntGroupRelease(ag);
- if (eg != NULL)
- IntGroupRelease(eg);
- return retval;
+ TransformFromValue(trans, &tr);
+ MolActionCreateAndPerform(mol, gMolActionAddSymmetryOperation, &tr);
+ return INT2NUM(mol->nsyms);
}
/*
* call-seq:
- * each_fragment(exclude = nil) {|group| ...}
+ * remove_symmetry(count = nil) -> Integer
+ * remove_symmetries(count = nil) -> Integer
*
- * Execute the block, with the IntGroup object for each fragment as the argument.
- * Atoms or bonds should not be added or removed during the execution of the block.
- * If exclude is given (as an array or an IntGroup), then those atoms are excluded
- * in defining the fragment.
+ * Remove the specified number of symmetry operations. The last added ones are removed
+ * first. If count is nil, then all symmetry operations are removed. Returns the
+ * number of leftover symmetries.
*/
static VALUE
-s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
+s_Molecule_RemoveSymmetry(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- IntGroup *ag, *fg, *eg;
- VALUE gval, exval;
+ VALUE cval;
+ int i, n;
Data_Get_Struct(self, Molecule, mol);
- if (mol == NULL || mol->natoms == 0)
- return self;
- rb_scan_args(argc, argv, "01", &exval);
- if (exval == Qnil)
- eg = NULL;
- else
- eg = IntGroupFromValue(exval);
- ag = IntGroupNewWithPoints(0, mol->natoms, -1);
- if (eg != NULL)
- IntGroupRemoveIntGroup(ag, eg);
- while (IntGroupGetCount(ag) > 0) {
- int n = IntGroupGetNthPoint(ag, 0);
- fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
- if (fg == NULL)
- rb_raise(rb_eMolbyError, "internal error during each_fragment");
- gval = ValueFromIntGroup(fg);
- rb_yield(gval);
- IntGroupRemoveIntGroup(ag, fg);
- IntGroupRelease(fg);
+ rb_scan_args(argc, argv, "01", &cval);
+ if (cval == Qnil)
+ n = mol->nsyms - 1;
+ else {
+ n = NUM2INT(rb_Integer(cval));
+ if (n < 0 || n > mol->nsyms)
+ rb_raise(rb_eMolbyError, "the given count of symops is out of range");
+ if (n == mol->nsyms)
+ n = mol->nsyms - 1;
}
- IntGroupRelease(ag);
- if (eg != NULL)
- IntGroupRelease(eg);
- return self;
+ for (i = 0; i < n; i++)
+ MolActionCreateAndPerform(mol, gMolActionDeleteSymmetryOperation);
+ return INT2NUM(mol->nsyms);
}
/*
* call-seq:
- * detachable?(group) -> [n1, n2]
+ * wrap_unit_cell(group) -> Vector3D
*
- * Check whether the group is 'detachable', i.e. the group is bound to the rest
- * of the molecule via only one bond. If it is, then the indices of the atoms
- * belonging to the bond is returned, the first element being the atom included
- * in the fragment. Otherwise, Qnil is returned.
+ * Move the specified group so that the center of mass of the group is within the
+ * unit cell. The offset vector is returned. If no periodic box is defined,
+ * exception is raised.
*/
static VALUE
-s_Molecule_Detachable_P(VALUE self, VALUE gval)
+s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
{
- Molecule *mol;
+ Molecule *mol;
IntGroup *ig;
- int n1, n2;
- VALUE retval;
+ Vector v, cv, dv;
Data_Get_Struct(self, Molecule, mol);
+ if (mol->cell == NULL)
+ rb_raise(rb_eMolbyError, "no unit cell is defined");
ig = s_Molecule_AtomGroupFromValue(self, gval);
- if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
- retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
- } else retval = Qnil;
+ s_Molecule_DoCenterOfMass(mol, &cv, ig);
+ TransformVec(&v, mol->cell->rtr, &cv);
+ if (mol->cell->flags[0])
+ v.x -= floor(v.x);
+ if (mol->cell->flags[1])
+ v.y -= floor(v.y);
+ if (mol->cell->flags[2])
+ v.z -= floor(v.z);
+ TransformVec(&dv, mol->cell->tr, &v);
+ VecDec(dv, cv);
+ MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
IntGroupRelease(ig);
- return retval;
+ return ValueFromVector(&dv);
}
/*
* call-seq:
- * bonds_on_border(group = selection) -> Array of Array of two Integers
+ * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0, allow_overlap = false) -> Array
*
- * Returns an array of bonds that connect an atom in the group and an atom out
- * of the group. The first atom in the bond always belongs to the group. If no
- * such bonds are present, an empty array is returned.
+ * Expand the specified part of the molecule by the given symmetry operation.
+ * Returns the array of atom indices corresponding to the expanded atoms.
+ * If allow_overlap is true, then new atoms are created even when the
+ * coordinates coincide with the some other atom (special position) of the
+ * same element; otherwise, such atom will not be created and the index of the
+ * existing atom is given in the returned array.
*/
static VALUE
-s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
+s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
{
- Molecule *mol;
- IntGroup *ig, *bg;
- VALUE gval, retval;
+ Molecule *mol;
+ VALUE gval, sval, xval, yval, zval, rval, oval;
+ IntGroup *ig;
+ Int n[4], allow_overlap;
+ Int natoms;
+ Int nidx, *idx;
+
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &gval);
- if (gval == Qnil) {
- ig = MoleculeGetSelection(mol);
- if (ig != NULL)
- IntGroupRetain(ig);
- } else {
- ig = s_Molecule_AtomGroupFromValue(self, gval);
- }
- retval = rb_ary_new();
- if (ig == NULL)
- return retval;
- bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
- if (bg != NULL) {
- IntGroupIterator iter;
- Int i;
- IntGroupIteratorInit(bg, &iter);
- while ((i = IntGroupIteratorNext(&iter)) >= 0) {
- /* The atoms at the border */
- Int n1, n2;
- n1 = mol->bonds[i * 2];
- n2 = mol->bonds[i * 2 + 1];
- if (IntGroupLookupPoint(ig, n1) < 0) {
- int w = n1;
- n1 = n2;
- n2 = w;
- if (IntGroupLookupPoint(ig, n1) < 0)
- continue; /* Actually this is an internal error */
- }
- rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
- }
- IntGroupIteratorRelease(&iter);
+ rb_scan_args(argc, argv, "24", &gval, &sval, &xval, &yval, &zval, &oval);
+ n[0] = NUM2INT(rb_Integer(sval));
+ n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
+ n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
+ n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
+ allow_overlap = (RTEST(oval) ? 1 : 0);
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
+ if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
+ rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
+ natoms = mol->natoms;
+
+ MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], allow_overlap, &nidx, &idx);
+
+ rval = rb_ary_new2(nidx);
+ while (--nidx >= 0) {
+ rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
}
- IntGroupRelease(bg);
- IntGroupRelease(ig);
- return retval;
+ /* if (natoms == mol->natoms)
+ rval = Qnil;
+ else {
+ rval = IntGroup_Alloc(rb_cIntGroup);
+ Data_Get_Struct(rval, IntGroup, ig);
+ IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
+ } */
+ return rval;
+}
+
+/*
+ * call-seq:
+ * amend_by_symmetry(group = nil) -> IntGroup
+ *
+ * Expand the specified part of the molecule by the given symmetry operation.
+ * Returns an IntGroup containing the added atoms.
+ */
+static VALUE
+s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ IntGroup *ig, *ig2;
+ VALUE rval, gval;
+ Data_Get_Struct(self, Molecule, mol);
+ rb_scan_args(argc, argv, "01", &gval);
+ if (gval != Qnil)
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
+ else ig = NULL;
+ MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
+ rval = ValueFromIntGroup(ig2);
+ IntGroupRelease(ig2);
+ return rval;
}
+#pragma mark ------ Transforms ------
+
/*
* call-seq:
* translate(vec, group = nil) -> Molecule
rb_scan_args(argc, argv, "11", &vec, &group);
ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
VectorFromValue(vec, &v);
-// MoleculeTranslate(mol, &v, ig);
+ // MoleculeTranslate(mol, &v, ig);
MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, ig);
if (ig != NULL)
IntGroupRelease(ig);
rb_scan_args(argc, argv, "11", &trans, &group);
ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
TransformFromValue(trans, &tr);
-/* MoleculeTransform(mol, tr, ig); */
+ /* MoleculeTransform(mol, tr, ig); */
MolActionCreateAndPerform(mol, gMolActionTransformAtoms, &tr, ig);
if (ig != NULL)
IntGroupRelease(ig);
return self;
}
-static void
-s_Molecule_DoCenterOfMass(Molecule *mol, Vector *outv, IntGroup *ig)
+/*
+ * call-seq:
+ * transform_for_symop(symop, is_cartesian = nil) -> Transform
+ *
+ * Get the transform corresponding to the symmetry operation. The symop can either be
+ * an integer (index of symmetry operation) or [sym, dx, dy, dz].
+ * If is_cartesian is true, the returned transform is for cartesian coordinates.
+ * Otherwise, the returned transform is for fractional coordinates.
+ * Raises exception when no cell or no transform are defined.
+ */
+static VALUE
+s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
{
- switch (MoleculeCenterOfMass(mol, outv, ig)) {
- case 2: rb_raise(rb_eMolbyError, "atom group is empty"); break;
- case 3: rb_raise(rb_eMolbyError, "weight is zero --- atomic weights are not defined?"); break;
- case 0: break;
- default: rb_raise(rb_eMolbyError, "cannot calculate center of mass"); break;
+ Molecule *mol;
+ VALUE sval, fval;
+ Symop symop;
+ Transform tr;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->cell == NULL)
+ rb_raise(rb_eMolbyError, "no unit cell is defined");
+ if (mol->nsyms == 0)
+ rb_raise(rb_eMolbyError, "no symmetry operation is defined");
+ rb_scan_args(argc, argv, "11", &sval, &fval);
+ if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
+ symop.sym = NUM2INT(rb_Integer(sval));
+ symop.dx = symop.dy = symop.dz = 0;
+ } else {
+ sval = rb_ary_to_ary(sval);
+ if (RARRAY_LEN(sval) < 4)
+ rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
+ symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
+ symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
+ symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
+ symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
}
+ if (symop.sym >= mol->nsyms)
+ rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
+ MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
+ return ValueFromTransform(&tr);
}
/*
* call-seq:
- * center_of_mass(group = nil) -> Vector3D
+ * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
*
- * Calculate the center of mass for the given set of atoms. The argument
- * group is null, then all atoms are considered.
+ * Get the symmetry operation corresponding to the given transform.
+ * If is_cartesian is true, the given transform is for cartesian coordinates.
+ * Otherwise, the given transform is for fractional coordinates.
+ * Raises exception when no cell or no transform are defined.
*/
static VALUE
-s_Molecule_CenterOfMass(int argc, VALUE *argv, VALUE self)
+s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE group;
- IntGroup *ig;
- Vector v;
+ VALUE tval, fval;
+ Symop symop;
+ Transform tr;
+ int n;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &group);
- ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
- s_Molecule_DoCenterOfMass(mol, &v, ig);
- if (ig != NULL)
- IntGroupRelease(ig);
- return ValueFromVector(&v);
+ if (mol->cell == NULL)
+ rb_raise(rb_eMolbyError, "no unit cell is defined");
+ if (mol->nsyms == 0)
+ rb_raise(rb_eMolbyError, "no symmetry operation is defined");
+ rb_scan_args(argc, argv, "11", &tval, &fval);
+ TransformFromValue(tval, &tr);
+ n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
+ if (n == 0) {
+ return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
+ } else {
+ return Qnil; /* Not found */
+ }
}
+#pragma mark ------ Frames ------
+
/*
* call-seq:
- * centralize(group = nil) -> self
+ * select_frame(index)
+ * frame = index
*
- * Translate the molecule so that the center of mass of the given group is located
- * at (0, 0, 0). Equivalent to molecule.translate(molecule.center_of_mass(group) * -1).
+ * Select the specified frame. If successful, returns true, otherwise returns false.
*/
static VALUE
-s_Molecule_Centralize(int argc, VALUE *argv, VALUE self)
+s_Molecule_SelectFrame(VALUE self, VALUE val)
{
Molecule *mol;
- VALUE group;
- IntGroup *ig;
- Vector v;
+ int ival = NUM2INT(val);
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &group);
- ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
- s_Molecule_DoCenterOfMass(mol, &v, ig);
- if (ig != NULL)
- IntGroupRelease(ig);
- v.x = -v.x;
- v.y = -v.y;
- v.z = -v.z;
- MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &v, NULL);
- return self;
+ ival = MoleculeSelectFrame(mol, ival, 1);
+ if (ival >= 0)
+ return Qtrue;
+ else return Qfalse;
}
/*
* call-seq:
- * bounds(group = nil) -> [min, max]
+ * frame -> Integer
*
- * Calculate the boundary. The return value is an array of two Vector3D objects.
+ * Get the current frame.
*/
static VALUE
-s_Molecule_Bounds(int argc, VALUE *argv, VALUE self)
+s_Molecule_Frame(VALUE self)
{
Molecule *mol;
- VALUE group;
+ Data_Get_Struct(self, Molecule, mol);
+ return INT2NUM(mol->cframe);
+}
+
+/*
+ * call-seq:
+ * nframes -> Integer
+ *
+ * Get the number of frames.
+ */
+static VALUE
+s_Molecule_Nframes(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ return INT2NUM(MoleculeGetNumberOfFrames(mol));
+}
+
+/*
+ * call-seq:
+ * insert_frame(integer, coordinates = nil, cell_axes = nil) -> bool
+ * insert_frames(intGroup = nil, coordinates = nil, cell_axes = nil) -> bool
+ *
+ * Insert new frames at the indices specified by the intGroup. If the first argument is
+ * an integer, a single new frame is inserted at that index. If the first argument is
+ * nil, a new frame is inserted at the last. If non-nil coordinates is given, it
+ * should be an array of arrays of Vector3Ds, then those coordinates are set
+ * to the new frame. Otherwise, the coordinates of current molecule are copied
+ * to the new frame.
+ * Returns an intGroup representing the inserted frames if successful, nil if not.
+ */
+static VALUE
+s_Molecule_InsertFrames(int argc, VALUE *argv, VALUE self)
+{
+ VALUE val, coords, cells;
+ Molecule *mol;
IntGroup *ig;
- Vector vmin, vmax;
- int n;
- Atom *ap;
+ int count, ival, i, j, len, len_c, len2, nframes;
+ VALUE *ptr, *ptr2;
+ Vector *vp, *vp2;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &group);
- ig = (NIL_P(group) ? NULL : s_Molecule_AtomGroupFromValue(self, group));
- if (ig != NULL && IntGroupGetCount(ig) == 0)
- rb_raise(rb_eMolbyError, "atom group is empty");
- vmin.x = vmin.y = vmin.z = 1e30;
- vmax.x = vmax.y = vmax.z = -1e30;
- for (n = 0, ap = mol->atoms; n < mol->natoms; n++, ap = ATOM_NEXT(ap)) {
- Vector r;
- if (ig != NULL && IntGroupLookup(ig, n, NULL) == 0)
- continue;
- r = ap->r;
- if (r.x < vmin.x)
- vmin.x = r.x;
- if (r.y < vmin.y)
- vmin.y = r.y;
- if (r.z < vmin.z)
- vmin.z = r.z;
- if (r.x > vmax.x)
- vmax.x = r.x;
- if (r.y > vmax.y)
- vmax.y = r.y;
- if (r.z > vmax.z)
- vmax.z = r.z;
+ rb_scan_args(argc, argv, "12", &val, &coords, &cells);
+ if (coords != Qnil) {
+ if (TYPE(coords) != T_ARRAY)
+ rb_raise(rb_eTypeError, "the coordinates should be given as an array of Vector3D");
+ len = RARRAY_LEN(coords);
+ } else len = 0;
+ if (cells != Qnil) {
+ if (mol->cell == NULL)
+ rb_raise(rb_eTypeError, "the unit cell is not defined but the cell axes are given");
+ if (TYPE(cells) != T_ARRAY)
+ rb_raise(rb_eTypeError, "the cell axes should be given as an array of Vector3D");
+ len_c = RARRAY_LEN(cells);
+ } else len_c = 0;
+ count = (len > len_c ? len : len_c); /* May be zero; will be updated later */
+ nframes = MoleculeGetNumberOfFrames(mol);
+ if (val == Qnil) {
+ ig = IntGroupNewWithPoints(nframes, (count > 0 ? count : 1), -1);
+ val = ValueFromIntGroup(ig);
+ } else {
+ ig = IntGroupFromValue(val);
+ }
+ count = IntGroupGetCount(ig); /* Count is updated here */
+ vp = ALLOC_N(Vector, mol->natoms * count);
+ if (cells != Qnil)
+ vp2 = ALLOC_N(Vector, 4 * count);
+ else vp2 = NULL;
+ if (len > 0) {
+ if (len < count)
+ rb_raise(rb_eMolbyError, "the coordinates should contain no less than %d arrays (for frames)", count);
+ ptr = RARRAY_PTR(coords);
+ for (i = 0; i < count; i++) {
+ if (TYPE(ptr[i]) != T_ARRAY)
+ rb_raise(rb_eTypeError, "the coordinate array contains non-array object at index %d", i);
+ len2 = RARRAY_LEN(ptr[i]);
+ if (len2 < mol->natoms)
+ rb_raise(rb_eMolbyError, "the array at index %d contains less than %d elements", i, mol->natoms);
+ ptr2 = RARRAY_PTR(ptr[i]);
+ for (j = 0; j < mol->natoms; j++)
+ VectorFromValue(ptr2[j], &vp[i * mol->natoms + j]);
+ }
+ } else {
+ Atom *ap;
+ for (i = 0; i < count; i++) {
+ for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
+ vp[i * mol->natoms + j] = ap->r;
+ }
+ }
+ }
+ if (len_c > 0) {
+ if (len_c < count)
+ rb_raise(rb_eMolbyError, "the cell vectors should contain no less than %d arrays (for frames)", count);
+ ptr = RARRAY_PTR(cells);
+ for (i = 0; i < count; i++) {
+ if (TYPE(ptr[i]) != T_ARRAY)
+ rb_raise(rb_eTypeError, "the cell parameter array contains non-array object at index %d", i);
+ len2 = RARRAY_LEN(ptr[i]);
+ if (len2 < 4)
+ rb_raise(rb_eMolbyError, "the cell parameter should contain 4 vectors");
+ ptr2 = RARRAY_PTR(ptr[i]);
+ for (j = 0; j < 4; j++)
+ VectorFromValue(ptr2[j], &vp2[i * 4 + j]);
+ }
}
- return rb_ary_new3(2, ValueFromVector(&vmin), ValueFromVector(&vmax));
+ ival = MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, mol->natoms * count, vp, (vp2 != NULL ? 4 * count : 0), vp2);
+ IntGroupRelease(ig);
+ xfree(vp);
+ if (vp2 != NULL)
+ xfree(vp2);
+ return (ival >= 0 ? val : Qnil);
}
-/* Get atom position or a vector */
-static void
-s_Molecule_GetVectorFromArg(Molecule *mol, VALUE val, Vector *vp)
+/*
+ * call-seq:
+ * create_frame(coordinates = nil) -> Integer
+ * create_frames(coordinates = nil) -> Integer
+ *
+ * Same as molecule.insert_frames(nil, coordinates).
+ */
+static VALUE
+s_Molecule_CreateFrames(int argc, VALUE *argv, VALUE self)
{
- if (rb_obj_is_kind_of(val, rb_cInteger) || rb_obj_is_kind_of(val, rb_cString)) {
- int n1 = s_Molecule_AtomIndexFromValue(mol, val);
- *vp = ATOM_AT_INDEX(mol->atoms, n1)->r;
- } else {
- VectorFromValue(val, vp);
- }
+ VALUE vals[3];
+ rb_scan_args(argc, argv, "02", &vals[1], &vals[2]);
+ vals[0] = Qnil;
+ return s_Molecule_InsertFrames(3, vals, self);
}
/*
* call-seq:
- * measure_bond(n1, n2) -> Float
+ * remove_frames(IntGroup, wantCoordinates = false)
*
- * Calculate the bond length. The arguments can either be atom indices, the "residue:name" representation,
- * or Vector3D values.
- * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ * Remove the frames at group. If wantsCoordinates is false (default), returns true if successful
+ * and nil otherwise. If wantsCoordinates is true, an array of arrays of the coordinates in the
+ * removed frames is returned if operation is successful.
*/
static VALUE
-s_Molecule_MeasureBond(VALUE self, VALUE nval1, VALUE nval2)
+s_Molecule_RemoveFrames(int argc, VALUE *argv, VALUE self)
{
+ VALUE val, flag;
+ VALUE retval;
Molecule *mol;
- Vector v1, v2;
+ IntGroup *ig;
+ int count;
Data_Get_Struct(self, Molecule, mol);
- s_Molecule_GetVectorFromArg(mol, nval1, &v1);
- s_Molecule_GetVectorFromArg(mol, nval2, &v2);
- return rb_float_new(MoleculeMeasureBond(mol, &v1, &v2));
+ rb_scan_args(argc, argv, "11", &val, &flag);
+ ig = IntGroupFromValue(val);
+ count = IntGroupGetCount(ig);
+ if (RTEST(flag)) {
+ /* Create return value before removing frames */
+ VALUE coords;
+ int i, j, n;
+ Atom *ap;
+ Vector v;
+ retval = rb_ary_new2(count);
+ for (i = 0; i < count; i++) {
+ n = IntGroupGetNthPoint(ig, i);
+ coords = rb_ary_new2(mol->natoms);
+ for (j = 0, ap = mol->atoms; j < mol->natoms; j++, ap = ATOM_NEXT(ap)) {
+ if (n < ap->nframes && n != mol->cframe)
+ v = ap->frames[n];
+ else v = ap->r;
+ rb_ary_push(coords, ValueFromVector(&v));
+ }
+ rb_ary_push(retval, coords);
+ }
+ } else retval = Qtrue;
+ if (MolActionCreateAndPerform(mol, gMolActionRemoveFrames, ig) >= 0)
+ return retval;
+ else return Qnil;
}
/*
* call-seq:
- * measure_angle(n1, n2, n3) -> Float
+ * each_frame {|n| ...}
*
- * Calculate the bond angle. The arguments can either be atom indices, the "residue:name" representation,
- * or Vector3D values. The return value is in degree.
- * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ * Set the frame number from 0 to nframes-1 and execute the block. The block argument is
+ * the frame number. After completion, the original frame number is restored.
*/
static VALUE
-s_Molecule_MeasureAngle(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3)
+s_Molecule_EachFrame(VALUE self)
{
+ int i, cframe, nframes;
Molecule *mol;
- Vector v1, v2, v3;
- Double d;
Data_Get_Struct(self, Molecule, mol);
- s_Molecule_GetVectorFromArg(mol, nval1, &v1);
- s_Molecule_GetVectorFromArg(mol, nval2, &v2);
- s_Molecule_GetVectorFromArg(mol, nval3, &v3);
- d = MoleculeMeasureAngle(mol, &v1, &v2, &v3);
- if (isnan(d))
- return Qnil; /* Cannot define */
- else return rb_float_new(d);
+ cframe = mol->cframe;
+ nframes = MoleculeGetNumberOfFrames(mol);
+ if (nframes > 0) {
+ for (i = 0; i < nframes; i++) {
+ MoleculeSelectFrame(mol, i, 1);
+ rb_yield(INT2NUM(i));
+ }
+ MoleculeSelectFrame(mol, cframe, 1);
+ }
+ return self;
}
/*
* call-seq:
- * measure_dihedral(n1, n2, n3, n4) -> Float
+ * get_coord_from_frame(index, group = nil)
*
- * Calculate the dihedral angle. The arguments can either be atom indices, the "residue:name" representation,
- * or Vector3D values. The return value is in degree.
- * If the crystallographic cell is defined, then the internal coordinates are convereted to the cartesian.
+ * Copy the coordinates from the indicated frame. If group is specified, only the specified atoms
+ * are modified. Third argument (cflag) is now obsolete (it used to specify whether the cell parameters are to be
+ * copied; now they are always copied)
*/
static VALUE
-s_Molecule_MeasureDihedral(VALUE self, VALUE nval1, VALUE nval2, VALUE nval3, VALUE nval4)
+s_Molecule_GetCoordFromFrame(int argc, VALUE *argv, VALUE self)
{
- Molecule *mol;
- Vector v1, v2, v3, v4;
- Double d;
+ Molecule *mol;
+ VALUE ival, gval, cval;
+ Int index, i, j, n, nn;
+ IntGroup *ig;
+ IntGroupIterator iter;
+ Atom *ap;
+ Vector *vp;
Data_Get_Struct(self, Molecule, mol);
- s_Molecule_GetVectorFromArg(mol, nval1, &v1);
- s_Molecule_GetVectorFromArg(mol, nval2, &v2);
- s_Molecule_GetVectorFromArg(mol, nval3, &v3);
- s_Molecule_GetVectorFromArg(mol, nval4, &v4);
- d = MoleculeMeasureDihedral(mol, &v1, &v2, &v3, &v4);
- if (isnan(d))
- return Qnil; /* Cannot define */
- else return rb_float_new(d);
+ rb_scan_args(argc, argv, "12", &ival, &gval, &cval);
+ if (argc == 3)
+ rb_warn("The 3rd argument to get_coord_from_frame() is now obsolete");
+ index = NUM2INT(rb_Integer(ival));
+ if (index < 0 || index >= (n = MoleculeGetNumberOfFrames(mol))) {
+ if (n == 0)
+ rb_raise(rb_eMolbyError, "No frame is present");
+ else
+ rb_raise(rb_eMolbyError, "Frame index (%d) out of range (should be 0..%d)", index, n - 1);
+ }
+ if (gval == Qnil) {
+ ig = IntGroupNewWithPoints(0, mol->natoms, -1);
+ } else {
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
+ }
+ n = IntGroupGetCount(ig);
+ if (n > 0) {
+ vp = (Vector *)calloc(sizeof(Vector), n);
+ IntGroupIteratorInit(ig, &iter);
+ j = 0;
+ nn = 0;
+ while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+ ap = ATOM_AT_INDEX(mol->atoms, i);
+ if (index < ap->nframes) {
+ vp[j] = ap->frames[index];
+ nn++;
+ } else {
+ vp[j] = ap->r;
+ }
+ j++;
+ }
+ if (nn > 0)
+ MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, n, vp);
+ free(vp);
+ if (mol->cell != NULL && mol->frame_cells != NULL && index < mol->nframe_cells) {
+ vp = mol->frame_cells + index * 4;
+ MolActionCreateAndPerform(mol, gMolActionSetBox, vp, vp + 1, vp + 2, vp + 3, -1, 0);
+ }
+ IntGroupIteratorRelease(&iter);
+ }
+ /* Copy the extra properties */
+ IntGroupRelease(ig);
+ for (i = 0; i < mol->nmolprops; i++) {
+ Double *dp = (Double *)malloc(sizeof(Double));
+ ig = IntGroupNew();
+ IntGroupAdd(ig, mol->cframe, 1);
+ *dp = mol->molprops[i].propvals[index];
+ MolActionCreateAndPerform(mol, gMolActionSetProperty, i, ig, 1, dp);
+ free(dp);
+ IntGroupRelease(ig);
+ }
+
+ return self;
}
/*
* call-seq:
- * expand_by_symmetry(group, sym, dx=0, dy=0, dz=0) -> Array
+ * reorder_frames(old_indices)
*
- * Expand the specified part of the molecule by the given symmetry operation.
- * Returns the array of atom indices corresponding to the expanded atoms.
+ * Reorder the frames. The argument is an array of integers that specify the 'old'
+ * frame numbers. Thus, if the argument is [2,0,1], then the new frames 0/1/2 are the
+ * same as the old frames 2/0/1, respectively.
+ * The argument must have the same number of integers as the number of frames.
*/
static VALUE
-s_Molecule_ExpandBySymmetry(int argc, VALUE *argv, VALUE self)
+s_Molecule_ReorderFrames(VALUE self, VALUE aval)
{
- Molecule *mol;
- VALUE gval, sval, xval, yval, zval, rval;
- IntGroup *ig;
- Int n[4];
- Int natoms;
- Int nidx, *idx;
-
+ Molecule *mol;
+ Int *ip, *ip2, i, n, nframes;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "23", &gval, &sval, &xval, &yval, &zval);
- n[0] = NUM2INT(rb_Integer(sval));
- n[1] = (xval == Qnil ? 0 : NUM2INT(rb_Integer(xval)));
- n[2] = (yval == Qnil ? 0 : NUM2INT(rb_Integer(yval)));
- n[3] = (zval == Qnil ? 0 : NUM2INT(rb_Integer(zval)));
- ig = s_Molecule_AtomGroupFromValue(self, gval);
- if (n[0] < 0 || (n[0] > 0 && n[0] >= mol->nsyms))
- rb_raise(rb_eMolbyError, "symmetry index is out of bounds");
- natoms = mol->natoms;
-
- MolActionCreateAndPerform(mol, gMolActionExpandBySymmetry, ig, n[1], n[2], n[3], n[0], &nidx, &idx);
-
- rval = rb_ary_new2(nidx);
- while (--nidx >= 0) {
- rb_ary_store(rval, nidx, INT2NUM(idx[nidx]));
+ aval = rb_ary_to_ary(aval);
+ nframes = MoleculeGetNumberOfFrames(mol);
+ if (RARRAY_LEN(aval) != nframes)
+ rb_raise(rb_eMolbyError, "The argument must have the same number of integers as the number of frames");
+ ip2 = (Int *)calloc(sizeof(Int), nframes);
+ ip = (Int *)calloc(sizeof(Int), nframes);
+ for (i = 0; i < nframes; i++) {
+ n = NUM2INT(rb_Integer(RARRAY_PTR(aval)[i]));
+ if (n < 0 || n >= nframes || ip2[n] != 0) {
+ free(ip2);
+ free(ip);
+ if (n < 0 || n >= nframes)
+ rb_raise(rb_eMolbyError, "The argument (%d) is out of range", n);
+ else
+ rb_raise(rb_eMolbyError, "The argument has duplicated entry (%d)", n);
+ }
+ ip2[n] = 1;
+ ip[i] = n;
}
-/* if (natoms == mol->natoms)
- rval = Qnil;
- else {
- rval = IntGroup_Alloc(rb_cIntGroup);
- Data_Get_Struct(rval, IntGroup, ig);
- IntGroup_RaiseIfError(IntGroupAdd(ig, natoms, mol->natoms - natoms));
- } */
- return rval;
+ free(ip2);
+ MolActionCreateAndPerform(mol, gMolActionReorderFrames, nframes, ip);
+ free(ip);
+ return self;
}
+#pragma mark ------ Fragments ------
+
/*
* call-seq:
- * amend_by_symmetry(group = nil) -> IntGroup
+ * fragment(n1, *exatoms) -> IntGroup
+ * fragment(group, *exatoms) -> IntGroup
*
- * Expand the specified part of the molecule by the given symmetry operation.
- * Returns an IntGroup containing the added atoms.
+ * Get the fragment including the atom n1 or the atom group. If additional arguments are given,
+ * those atoms will not be counted during the search.
*/
static VALUE
-s_Molecule_AmendBySymmetry(int argc, VALUE *argv, VALUE self)
+s_Molecule_Fragment(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- IntGroup *ig, *ig2;
- VALUE rval, gval;
+ IntGroup *baseg, *ig, *exatoms;
+ int n;
+ volatile VALUE nval, exval;
Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "01", &gval);
- if (gval != Qnil)
- ig = s_Molecule_AtomGroupFromValue(self, gval);
- else ig = NULL;
- MolActionCreateAndPerform(mol, gMolActionAmendBySymmetry, ig, &ig2);
- rval = ValueFromIntGroup(ig2);
- IntGroupRelease(ig2);
- return rval;
+ rb_scan_args(argc, argv, "1*", &nval, &exval);
+ if (rb_obj_is_kind_of(nval, rb_cNumeric) || rb_obj_is_kind_of(nval, rb_cString)) {
+ baseg = NULL;
+ n = NUM2INT(s_Molecule_AtomIndex(self, nval));
+ } else {
+ baseg = s_Molecule_AtomGroupFromValue(self, nval);
+ }
+ if (RARRAY_LEN(exval) == 0) {
+ exatoms = NULL;
+ } else {
+ exval = s_Molecule_AtomGroup(RARRAY_LEN(exval), RARRAY_PTR(exval), self);
+ Data_Get_Struct(exval, IntGroup, exatoms);
+ }
+ if (baseg == NULL) {
+ ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+ } else {
+ IntGroupIterator iter;
+ IntGroupIteratorInit(baseg, &iter);
+ if ((n = IntGroupIteratorNext(&iter)) < 0) {
+ ig = IntGroupNew();
+ } else {
+ ig = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+ if (ig != NULL) {
+ while ((n = IntGroupIteratorNext(&iter)) >= 0) {
+ IntGroup *subg;
+ subg = MoleculeFragmentExcludingAtomGroup(mol, n, exatoms);
+ if (subg != NULL) {
+ IntGroupAddIntGroup(ig, subg);
+ IntGroupRelease(subg);
+ }
+ }
+ }
+ }
+ IntGroupIteratorRelease(&iter);
+ IntGroupRelease(baseg);
+ }
+ if (ig == NULL)
+ rb_raise(rb_eMolbyError, "invalid specification of molecular fragment");
+ nval = ValueFromIntGroup(ig);
+ IntGroupRelease(ig);
+ return nval;
}
/*
* call-seq:
- * transform_for_symop(symop, is_cartesian = nil) -> Transform
+ * fragments(exclude = nil)
*
- * Get the transform corresponding to the symmetry operation. The symop can either be
- * an integer (index of symmetry operation) or [sym, dx, dy, dz].
- * If is_cartesian is true, the returned transform is for cartesian coordinates.
- * Otherwise, the returned transform is for fractional coordinates.
- * Raises exception when no cell or no transform are defined.
+ * Returns the fragments as an array of IntGroups.
+ * If exclude is given (as an array or an IntGroup), then those atoms are excluded
+ * in defining the fragment.
*/
static VALUE
-s_Molecule_TransformForSymop(int argc, VALUE *argv, VALUE self)
+s_Molecule_Fragments(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE sval, fval;
- Symop symop;
- Transform tr;
+ IntGroup *ag, *fg, *eg;
+ VALUE gval, exval, retval;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- rb_raise(rb_eMolbyError, "no unit cell is defined");
- if (mol->nsyms == 0)
- rb_raise(rb_eMolbyError, "no symmetry operation is defined");
- rb_scan_args(argc, argv, "11", &sval, &fval);
- if (rb_obj_is_kind_of(sval, rb_cNumeric)) {
- symop.sym = NUM2INT(rb_Integer(sval));
- symop.dx = symop.dy = symop.dz = 0;
- } else {
- sval = rb_ary_to_ary(sval);
- if (RARRAY_LEN(sval) < 4)
- rb_raise(rb_eMolbyError, "missing arguments as symop; at least four integers should be given");
- symop.sym = NUM2INT(rb_Integer(RARRAY_PTR(sval)[0]));
- symop.dx = NUM2INT(rb_Integer(RARRAY_PTR(sval)[1]));
- symop.dy = NUM2INT(rb_Integer(RARRAY_PTR(sval)[2]));
- symop.dz = NUM2INT(rb_Integer(RARRAY_PTR(sval)[3]));
+ if (mol == NULL)
+ return Qnil;
+ if (mol->natoms == 0)
+ return rb_ary_new();
+ rb_scan_args(argc, argv, "01", &exval);
+ if (exval == Qnil)
+ eg = NULL;
+ else
+ eg = IntGroupFromValue(exval);
+ ag = IntGroupNewWithPoints(0, mol->natoms, -1);
+ if (eg != NULL)
+ IntGroupRemoveIntGroup(ag, eg);
+ retval = rb_ary_new();
+ while (IntGroupGetCount(ag) > 0) {
+ int n = IntGroupGetNthPoint(ag, 0);
+ fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
+ if (fg == NULL)
+ rb_raise(rb_eMolbyError, "internal error during each_fragment");
+ gval = ValueFromIntGroup(fg);
+ rb_ary_push(retval, gval);
+ IntGroupRemoveIntGroup(ag, fg);
+ IntGroupRelease(fg);
}
- if (symop.sym >= mol->nsyms)
- rb_raise(rb_eMolbyError, "index of symmetry operation (%d) is out of range", symop.sym);
- MoleculeGetTransformForSymop(mol, symop, &tr, (RTEST(fval) != 0));
- return ValueFromTransform(&tr);
+ IntGroupRelease(ag);
+ if (eg != NULL)
+ IntGroupRelease(eg);
+ return retval;
}
-
+
/*
* call-seq:
- * symop_for_transform(transform, is_cartesian = nil) -> [sym, dx, dy, dz]
+ * each_fragment(exclude = nil) {|group| ...}
*
- * Get the symmetry operation corresponding to the given transform.
- * If is_cartesian is true, the given transform is for cartesian coordinates.
- * Otherwise, the given transform is for fractional coordinates.
- * Raises exception when no cell or no transform are defined.
+ * Execute the block, with the IntGroup object for each fragment as the argument.
+ * Atoms or bonds should not be added or removed during the execution of the block.
+ * If exclude is given (as an array or an IntGroup), then those atoms are excluded
+ * in defining the fragment.
*/
static VALUE
-s_Molecule_SymopForTransform(int argc, VALUE *argv, VALUE self)
+s_Molecule_EachFragment(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
- VALUE tval, fval;
- Symop symop;
- Transform tr;
- int n;
+ IntGroup *ag, *fg, *eg;
+ VALUE gval, exval;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- rb_raise(rb_eMolbyError, "no unit cell is defined");
- if (mol->nsyms == 0)
- rb_raise(rb_eMolbyError, "no symmetry operation is defined");
- rb_scan_args(argc, argv, "11", &tval, &fval);
- TransformFromValue(tval, &tr);
- n = MoleculeGetSymopForTransform(mol, tr, &symop, (RTEST(fval) != 0));
- if (n == 0) {
- return rb_ary_new3(4, INT2NUM(symop.sym), INT2NUM(symop.dx), INT2NUM(symop.dy), INT2NUM(symop.dz));
- } else {
- return Qnil; /* Not found */
+ if (mol == NULL || mol->natoms == 0)
+ return self;
+ rb_scan_args(argc, argv, "01", &exval);
+ if (exval == Qnil)
+ eg = NULL;
+ else
+ eg = IntGroupFromValue(exval);
+ ag = IntGroupNewWithPoints(0, mol->natoms, -1);
+ if (eg != NULL)
+ IntGroupRemoveIntGroup(ag, eg);
+ while (IntGroupGetCount(ag) > 0) {
+ int n = IntGroupGetNthPoint(ag, 0);
+ fg = MoleculeFragmentExcludingAtomGroup(mol, n, eg);
+ if (fg == NULL)
+ rb_raise(rb_eMolbyError, "internal error during each_fragment");
+ gval = ValueFromIntGroup(fg);
+ rb_yield(gval);
+ IntGroupRemoveIntGroup(ag, fg);
+ IntGroupRelease(fg);
}
+ IntGroupRelease(ag);
+ if (eg != NULL)
+ IntGroupRelease(eg);
+ return self;
}
/*
* call-seq:
- * wrap_unit_cell(group) -> Vector3D
+ * detachable?(group) -> [n1, n2]
*
- * Move the specified group so that the center of mass of the group is within the
- * unit cell. The offset vector is returned. If no periodic box is defined,
- * exception is raised.
+ * Check whether the group is 'detachable', i.e. the group is bound to the rest
+ * of the molecule via only one bond. If it is, then the indices of the atoms
+ * belonging to the bond is returned, the first element being the atom included
+ * in the fragment. Otherwise, Qnil is returned.
*/
static VALUE
-s_Molecule_WrapUnitCell(VALUE self, VALUE gval)
+s_Molecule_Detachable_P(VALUE self, VALUE gval)
{
- Molecule *mol;
+ Molecule *mol;
IntGroup *ig;
- Vector v, cv, dv;
+ int n1, n2;
+ VALUE retval;
Data_Get_Struct(self, Molecule, mol);
- if (mol->cell == NULL)
- rb_raise(rb_eMolbyError, "no unit cell is defined");
ig = s_Molecule_AtomGroupFromValue(self, gval);
- s_Molecule_DoCenterOfMass(mol, &cv, ig);
- TransformVec(&v, mol->cell->rtr, &cv);
- if (mol->cell->flags[0])
- v.x -= floor(v.x);
- if (mol->cell->flags[1])
- v.y -= floor(v.y);
- if (mol->cell->flags[2])
- v.z -= floor(v.z);
- TransformVec(&dv, mol->cell->tr, &v);
- VecDec(dv, cv);
- MolActionCreateAndPerform(mol, gMolActionTranslateAtoms, &dv, ig);
+ if (MoleculeIsFragmentDetachable(mol, ig, &n1, &n2)) {
+ retval = rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2));
+ } else retval = Qnil;
IntGroupRelease(ig);
- return ValueFromVector(&dv);
+ return retval;
}
/*
* call-seq:
- * find_conflicts(limit[, group1[, group2 [, ignore_exclusion]]]) -> [[n1, n2], [n3, n4], ...]
- *
- * Find pairs of atoms that are within the limit distance. If group1 and group2 are given, the
- * first and second atom in the pair should belong to group1 and group2, respectively.
- * If ignore_exclusion is true, then 1-2 (bonded), 1-3, 1-4 pairs are also considered.
- */
-static VALUE
-s_Molecule_FindConflicts(int argc, VALUE *argv, VALUE self)
-{
- Molecule *mol;
- VALUE limval, gval1, gval2, rval, igval;
- IntGroup *ig1, *ig2;
- IntGroupIterator iter1, iter2;
- Int npairs, *pairs;
- Int n[2], i;
- Double lim;
- Vector r1;
- Atom *ap1, *ap2;
- MDExclusion *exinfo;
- Int *exlist;
-
- Data_Get_Struct(self, Molecule, mol);
- rb_scan_args(argc, argv, "13", &limval, &gval1, &gval2, &igval);
- lim = NUM2DBL(rb_Float(limval));
- if (lim <= 0.0)
- rb_raise(rb_eMolbyError, "the limit (%g) should be positive", lim);
- if (gval1 != Qnil)
- ig1 = s_Molecule_AtomGroupFromValue(self, gval1);
- else
- ig1 = IntGroupNewWithPoints(0, mol->natoms, -1);
- if (gval2 != Qnil)
- ig2 = s_Molecule_AtomGroupFromValue(self, gval2);
- else
- ig2 = IntGroupNewWithPoints(0, mol->natoms, -1);
-
- if (!RTEST(igval)) {
- /* Use the exclusion table in MDArena */
- if (mol->par == NULL || mol->arena == NULL || mol->arena->is_initialized == 0 || mol->needsMDRebuild) {
- VALUE mval = ValueFromMolecule(mol);
- s_RebuildMDParameterIfNecessary(mval, Qnil);
- }
- exinfo = mol->arena->exinfo; /* May be NULL */
- exlist = mol->arena->exlist;
- } else {
- exinfo = NULL;
- exlist = NULL;
- }
- IntGroupIteratorInit(ig1, &iter1);
- IntGroupIteratorInit(ig2, &iter2);
- npairs = 0;
- pairs = NULL;
- while ((n[0] = IntGroupIteratorNext(&iter1)) >= 0) {
- Int exn1, exn2;
- ap1 = ATOM_AT_INDEX(mol->atoms, n[0]);
- r1 = ap1->r;
- if (exinfo != NULL) {
- exn1 = exinfo[n[0]].index1;
- exn2 = exinfo[n[0] + 1].index1;
- } else exn1 = exn2 = -1;
- IntGroupIteratorReset(&iter2);
- while ((n[1] = IntGroupIteratorNext(&iter2)) >= 0) {
- ap2 = ATOM_AT_INDEX(mol->atoms, n[1]);
- if (n[0] == n[1])
- continue; /* Same atom */
- if (exinfo != NULL) {
- /* Look up exclusion table to exclude 1-2, 1-3, and 1-4 pairs */
- for (i = exn1; i < exn2; i++) {
- if (exlist[i] == n[1])
- break;
- }
- if (i < exn2)
- continue; /* Should be excluded */
- }
- if (MoleculeMeasureBond(mol, &r1, &(ap2->r)) < lim) {
- /* Is this pair already registered? */
- Int *ip;
- for (i = 0, ip = pairs; i < npairs; i++, ip += 2) {
- if ((ip[0] == n[0] && ip[1] == n[1]) || (ip[0] == n[1] && ip[1] == n[0]))
- break;
- }
- if (i >= npairs) {
- /* Not registered yet */
- AssignArray(&pairs, &npairs, sizeof(Int) * 2, npairs, n);
- }
- }
- }
+ * bonds_on_border(group = selection) -> Array of Array of two Integers
+ *
+ * Returns an array of bonds that connect an atom in the group and an atom out
+ * of the group. The first atom in the bond always belongs to the group. If no
+ * such bonds are present, an empty array is returned.
+ */
+static VALUE
+s_Molecule_BondsOnBorder(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ IntGroup *ig, *bg;
+ VALUE gval, retval;
+ Data_Get_Struct(self, Molecule, mol);
+ rb_scan_args(argc, argv, "01", &gval);
+ if (gval == Qnil) {
+ ig = MoleculeGetSelection(mol);
+ if (ig != NULL)
+ IntGroupRetain(ig);
+ } else {
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
}
- IntGroupIteratorRelease(&iter2);
- IntGroupIteratorRelease(&iter1);
- IntGroupRelease(ig2);
- IntGroupRelease(ig1);
- rval = rb_ary_new2(npairs);
- if (pairs != NULL) {
- for (i = 0; i < npairs; i++) {
- rb_ary_push(rval, rb_ary_new3(2, INT2NUM(pairs[i * 2]), INT2NUM(pairs[i * 2 + 1])));
+ retval = rb_ary_new();
+ if (ig == NULL)
+ return retval;
+ bg = MoleculeSearchBondsAcrossAtomGroup(mol, ig);
+ if (bg != NULL) {
+ IntGroupIterator iter;
+ Int i;
+ IntGroupIteratorInit(bg, &iter);
+ while ((i = IntGroupIteratorNext(&iter)) >= 0) {
+ /* The atoms at the border */
+ Int n1, n2;
+ n1 = mol->bonds[i * 2];
+ n2 = mol->bonds[i * 2 + 1];
+ if (IntGroupLookupPoint(ig, n1) < 0) {
+ int w = n1;
+ n1 = n2;
+ n2 = w;
+ if (IntGroupLookupPoint(ig, n1) < 0)
+ continue; /* Actually this is an internal error */
+ }
+ rb_ary_push(retval, rb_ary_new3(2, INT2NUM(n1), INT2NUM(n2)));
}
- free(pairs);
+ IntGroupIteratorRelease(&iter);
}
- return rval;
+ IntGroupRelease(bg);
+ IntGroupRelease(ig);
+ return retval;
}
/* Calculate the transform that moves the current coordinates to the reference
VALUE gval, rval, wval;
IntGroup *ig;
IntGroupIterator iter;
- int nn, errno, i, j, in, status;
+ int nn, errnum, i, j, in, status;
Vector *ref;
Double *weights, dval[3];
Transform tr;
if (gval == Qnil)
ig = IntGroupNewWithPoints(0, mol->natoms, -1);
else
- ig = IntGroupFromValue(gval);
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
if (ig == NULL || (nn = IntGroupGetCount(ig)) == 0) {
IntGroupRelease(ig);
rb_raise(rb_eMolbyError, "atom group is not given correctly");
if (rb_obj_is_kind_of(rval, rb_cNumeric)) {
int fn = NUM2INT(rb_Integer(rval));
if (fn < 0 || fn >= MoleculeGetNumberOfFrames(mol)) {
- errno = 1;
+ errnum = 1;
status = fn;
goto err;
}
} else if (rb_obj_is_kind_of(rval, rb_cLAMatrix)) {
LAMatrix *m = LAMatrixFromValue(rval, NULL, 0, 0);
if (m->row * m->column < nn * 3) {
- errno = 2;
+ errnum = 2;
goto err;
}
for (i = 0; i < nn; i++) {
VALUE aval;
rval = rb_protect(rb_ary_to_ary, rval, &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
if (RARRAY_LEN(rval) < nn) {
- errno = 2;
+ errnum = 2;
goto err;
}
if (rb_obj_is_kind_of((RARRAY_PTR(rval))[0], rb_cNumeric)) {
/* Array of 3*nn numbers */
if (RARRAY_LEN(rval) < nn * 3) {
- errno = 2;
+ errnum = 2;
goto err;
}
for (i = 0; i < nn; i++) {
for (j = 0; j < 3; j++) {
aval = rb_protect(rb_Float, (RARRAY_PTR(rval))[i * 3 + j], &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
dval[j] = NUM2DBL(aval);
} else {
aval = rb_protect(rb_ary_to_ary, aval, &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
if (RARRAY_LEN(aval) < 3) {
- errno = 4;
+ errnum = 4;
status = i;
goto err;
}
for (j = 0; j < 3; j++) {
VALUE aaval = rb_protect(rb_Float, (RARRAY_PTR(aval))[j], &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
dval[j] = NUM2DBL(aaval);
} else {
wval = rb_protect(rb_ary_to_ary, wval, &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
if (RARRAY_LEN(wval) < nn) {
- errno = 5;
+ errnum = 5;
goto err;
}
for (i = 0; i < nn; i++) {
VALUE wwval = rb_protect(rb_Float, (RARRAY_PTR(wval))[i], &status);
if (status != 0) {
- errno = 3;
+ errnum = 3;
goto err;
}
weights[i] = NUM2DBL(wwval);
}
dval[0] = s_Molecule_FitCoordinates_Sub(mol, ig, ref, weights, tr);
if (dval[0] < 0) {
- errno = 6;
+ errnum = 6;
goto err;
}
- errno = 0;
+ errnum = 0;
err:
IntGroupIteratorRelease(&iter);
free(ref);
free(weights);
- if (errno == 0) {
+ if (errnum == 0) {
return rb_ary_new3(2, ValueFromTransform(&tr), rb_float_new(dval[0]));
- } else if (errno == 1) {
+ } else if (errnum == 1) {
rb_raise(rb_eMolbyError, "frame index (%d) is out of range", status);
- } else if (errno == 2) {
+ } else if (errnum == 2) {
rb_raise(rb_eMolbyError, "insufficient number of reference coordinates");
- } else if (errno == 3) {
+ } else if (errnum == 3) {
rb_jump_tag(status);
- } else if (errno == 4) {
+ } else if (errnum == 4) {
rb_raise(rb_eMolbyError, "less than 3 elements for index %d of reference coordinates", status);
- } else if (errno == 5) {
+ } else if (errnum == 5) {
rb_raise(rb_eMolbyError, "insufficient number of weight values");
- } else if (errno == 6) {
+ } else if (errnum == 6) {
rb_raise(rb_eMolbyError, "matrix calculation failed during coordinate fitting");
}
return Qnil; /* Not reached */
}
+#pragma mark ------ Screen Display ------
+
/*
* call-seq:
* display
/*
* call-seq:
+ * hidden_atoms -> IntGroup
+ *
+ * Returns the currently hidden atoms.
+ */
+static VALUE
+s_Molecule_HiddenAtoms(VALUE self)
+{
+ rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
+ return Qnil; /* Not reached */
+}
+
+/*
+ * call-seq:
+ * set_hidden_atoms(IntGroup)
+ * self.hidden_atoms = IntGroup
+ *
+ * Hide the specified atoms. This operation is _not_ undoable.
+ */
+static VALUE
+s_Molecule_SetHiddenAtoms(VALUE self, VALUE val)
+{
+ rb_raise(rb_eMolbyError, "set_hidden_atoms is now obsolete. Try using Molecule#is_atom_visible or AtomRef#hidden.");
+ return Qnil; /* Not reached */
+}
+
+/*
+ * call-seq:
* show_graphite -> Integer
* show_graphite = Integer
* show_graphite = boolean
/*
* call-seq:
+ * show_rotation_center -> [amin, amax, bmin, bmax, cmin, cmax]
+ * show_rotation_center = [amin, amax, bmin, bmax, cmin, cmax]
+ * show_rotation_center = boolean
+ *
+ * Set to show the rotation center of the screen.
+ * If the argument is boolean, only the show/hide flag is modified.
+ */
+static VALUE
+s_Molecule_ShowRotationCenter(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ return Qnil;
+ if (argc > 0) {
+ mol->mview->showRotationCenter = (RTEST(argv[0]) != 0);
+ MainViewCallback_setNeedsDisplay(mol->mview, 1);
+ }
+ return (mol->mview->showRotationCenter ? Qtrue : Qfalse);
+}
+
+/*
+ * call-seq:
* line_mode
* line_mode(bool)
* line_mode = bool
/*
* call-seq:
+ * atom_radius = float
+ * atom_radius
+ *
+ * Set the atom radius (multiple of the vdw radius) used in drawing the model in normal (non-line) mode.
+ * (Default = 0.4)
+ * If no argument is given, the current value is returned.
+ */
+static VALUE
+s_Molecule_AtomRadius(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ return Qnil;
+ if (argc > 0) {
+ double rad = NUM2DBL(rb_Float(argv[0]));
+ if (rad > 0.0) {
+ mol->mview->atomRadius = rad;
+ MainViewCallback_setNeedsDisplay(mol->mview, 1);
+ }
+ return argv[0];
+ }
+ return rb_float_new(mol->mview->atomRadius);
+}
+
+/*
+ * call-seq:
+ * bond_radius = float
+ * bond_radius
+ *
+ * Set the bond radius (in angstrom) used in drawing the model in normal (non-line) mode. (Default = 0.1)
+ * If no argument is given, the current value is returned.
+ */
+static VALUE
+s_Molecule_BondRadius(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ return Qnil;
+ if (argc > 0) {
+ double rad = NUM2DBL(rb_Float(argv[0]));
+ if (rad > 0.0) {
+ mol->mview->bondRadius = rad;
+ MainViewCallback_setNeedsDisplay(mol->mview, 1);
+ }
+ return argv[0];
+ }
+ return rb_float_new(mol->mview->bondRadius);
+}
+
+/*
+ * call-seq:
+ * atom_resolution = integer
+ * atom_resolution
+ *
+ * Set the atom resolution used in drawing the model in normal (non-line) mode.
+ * (Default = 12; minimum = 6)
+ * If no argument is given, the current value is returned.
+ */
+static VALUE
+s_Molecule_AtomResolution(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ return Qnil;
+ if (argc > 0) {
+ int res = NUM2INT(rb_Integer(argv[0]));
+ if (res < 6)
+ rb_raise(rb_eRangeError, "The atom resolution (%d) less than 6 is not acceptable", res);
+ mol->mview->atomResolution = res;
+ MainViewCallback_setNeedsDisplay(mol->mview, 1);
+ return INT2NUM(res);
+ }
+ return INT2NUM(mol->mview->atomResolution);
+}
+
+/*
+ * call-seq:
+ * bond_resolution = integer
+ * bond_resolution
+ *
+ * Set the bond resolution used in drawing the model in normal (non-line) mode.
+ * (Default = 8; minimum = 4)
+ * If no argument is given, the current value is returned.
+ */
+static VALUE
+s_Molecule_BondResolution(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ return Qnil;
+ if (argc > 0) {
+ int res = NUM2INT(rb_Integer(argv[0]));
+ if (res < 4)
+ rb_raise(rb_eRangeError, "The bond resolution (%d) less than 4 is not acceptable", res);
+ mol->mview->bondResolution = res;
+ MainViewCallback_setNeedsDisplay(mol->mview, 1);
+ return INT2NUM(res);
+ }
+ return INT2NUM(mol->mview->bondResolution);
+}
+
+/*
+ * call-seq:
* resize_to_fit
*
* Resize the model drawing to fit in the window.
s_Molecule_GetViewRotation(VALUE self)
{
Molecule *mol;
- float f[4];
+ double f[4];
Vector v;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
s_Molecule_GetViewCenter(VALUE self)
{
Molecule *mol;
- float f[4];
+ double f[4];
Vector v;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
s_Molecule_SetViewRotation(VALUE self, VALUE aval, VALUE angval)
{
Molecule *mol;
- float f[4];
+ double f[4];
Vector v;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
{
Molecule *mol;
Vector v;
- float f[4];
+ double f[4];
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
return Qnil;
/*
* call-seq:
- * create_graphic(kind, color, points, fill = nil) -> integer
+ * export_graphic(fname, scale = 1.0, bg_color = -1, width = 0, height = 0)
+ *
+ * Export the current graphic to a PNG or TIF file (determined by the extension).
+ * bg_color: -1, same as screen; 0, transparent; 1, black; 2, white.
+ * If either width or height is not specified, then the screen width/height is used instead.
+ */
+static VALUE
+s_Molecule_ExportGraphic(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ VALUE fval, sval, bval, wval, hval;
+ char *fname;
+ float scale;
+ int bg_color, width, height;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ rb_raise(rb_eMolbyError, "The molecule has no associated graphic view");
+ rb_scan_args(argc, argv, "14", &fval, &sval, &bval, &wval, &hval);
+ fname = FileStringValuePtr(fval);
+ if (sval == Qnil)
+ scale = 1.0;
+ else scale = NUM2DBL(rb_Float(sval));
+ if (bval == Qnil)
+ bg_color = -1;
+ else bg_color = NUM2INT(rb_Integer(bval));
+ if (wval == Qnil)
+ width = 0;
+ else width = NUM2INT(rb_Integer(wval));
+ if (hval == Qnil)
+ height = 0;
+ else height = NUM2INT(rb_Integer(hval));
+ if (MainViewCallback_exportGraphic(mol->mview, fname, scale, bg_color, width, height) == 0)
+ return fval;
+ else return Qnil;
+}
+
+#pragma mark ------ Graphics ------
+
+static void
+s_CalculateGraphicNormals(MainViewGraphic *gp)
+{
+ int i;
+ Vector v1, v2, v3;
+ if (gp == NULL || gp->npoints < 3)
+ return;
+ AssignArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, gp->npoints - 1, NULL);
+ v1.x = gp->points[3] - gp->points[0];
+ v1.y = gp->points[4] - gp->points[1];
+ v1.z = gp->points[5] - gp->points[2];
+ /* nv[i] = (v[i-1]-v[0]).cross(v[i]-v[0]) (i=2..n-1) */
+ for (i = 2; i < gp->npoints; i++) {
+ v2.x = gp->points[i * 3] - gp->points[0];
+ v2.y = gp->points[i * 3 + 1] - gp->points[1];
+ v2.z = gp->points[i * 3 + 2] - gp->points[2];
+ VecCross(v3, v1, v2);
+ NormalizeVec(&v3, &v3);
+ gp->normals[i * 3] = v3.x;
+ gp->normals[i * 3 + 1] = v3.y;
+ gp->normals[i * 3 + 2] = v3.z;
+ v1 = v2;
+ }
+ /* normals[0] = average of all nv[i] (i=2..n-1) */
+ VecZero(v1);
+ for (i = 2; i < gp->npoints; i++) {
+ v1.x += gp->normals[i * 3];
+ v1.y += gp->normals[i * 3 + 1];
+ v1.z += gp->normals[i * 3 + 2];
+ }
+ NormalizeVec(&v1, &v1);
+ gp->normals[0] = v1.x;
+ gp->normals[1] = v1.y;
+ gp->normals[2] = v1.z;
+ /* normals[1] = nv[2].normalize */
+ v2.x = gp->normals[6];
+ v2.y = gp->normals[7];
+ v2.z = gp->normals[8];
+ NormalizeVec(&v1, &v2);
+ gp->normals[3] = v1.x;
+ gp->normals[4] = v1.y;
+ gp->normals[5] = v1.z;
+ /* normals[i] = (nv[i] + nv[i+1]).normalize (i=2..n-2) */
+ for (i = 2; i < gp->npoints; i++) {
+ if (i == gp->npoints - 1)
+ VecZero(v3);
+ else {
+ v3.x = gp->normals[i * 3 + 3];
+ v3.y = gp->normals[i * 3 + 4];
+ v3.z = gp->normals[i * 3 + 5];
+ }
+ VecInc(v2, v3);
+ NormalizeVec(&v1, &v2);
+ gp->normals[i * 3] = v1.x;
+ gp->normals[i * 3 + 1] = v1.y;
+ gp->normals[i * 3 + 2] = v1.z;
+ v2 = v3;
+ }
+}
+
+/*
+ * call-seq:
+ * insert_graphic(index, kind, color, points, fill = nil) -> integer
*
- * Create a new graphic object.
+ * Create a new graphic object and insert at the given graphic index (if -1, then append at the last).
* kind: a symbol representing the kind of the graphic. :line, :poly, :cylinder, :cone, :ellipsoid
* color: an array of 3 (rgb) or 4 (rgba) floating numbers
* points: an array of Vectors
*
*/
static VALUE
-s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
+s_Molecule_InsertGraphic(int argc, VALUE *argv, VALUE self)
{
Molecule *mol;
MainViewGraphic g;
- int i, n, ni;
+ int i, n, ni, idx;
const char *p;
- VALUE kval, cval, pval, fval;
+ VALUE kval, cval, pval, fval, ival;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
- rb_scan_args(argc, argv, "31", &kval, &cval, &pval, &fval);
- kval = rb_obj_as_string(kval);
+ rb_scan_args(argc, argv, "41", &ival, &kval, &cval, &pval, &fval);
+ idx = NUM2INT(rb_Integer(ival));
+ if (idx == -1)
+ idx = mol->mview->ngraphics;
+ else if (idx < 0 || idx > mol->mview->ngraphics)
+ rb_raise(rb_eMolbyError, "the graphic index (%d) out of range", idx);
memset(&g, 0, sizeof(g));
g.visible = 1;
- p = RSTRING_PTR(kval);
- if (strcmp(p, "line") == 0)
- g.kind = kMainViewGraphicLine;
- else if (strcmp(p, "poly") == 0)
- g.kind = kMainViewGraphicPoly;
- else if (strcmp(p, "cylinder") == 0)
- g.kind = kMainViewGraphicCylinder;
- else if (strcmp(p, "cone") == 0)
- g.kind = kMainViewGraphicCone;
- else if (strcmp(p, "ellipsoid") == 0)
- g.kind = kMainViewGraphicEllipsoid;
- else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
+ if (rb_obj_is_kind_of(kval, rb_cInteger)) {
+ g.kind = NUM2INT(kval); /* Direct assign (for undo registration) */
+ } else {
+ kval = rb_obj_as_string(kval);
+ p = StringValuePtr(kval);
+ if (strcmp(p, "line") == 0)
+ g.kind = kMainViewGraphicLine;
+ else if (strcmp(p, "poly") == 0)
+ g.kind = kMainViewGraphicPoly;
+ else if (strcmp(p, "cylinder") == 0)
+ g.kind = kMainViewGraphicCylinder;
+ else if (strcmp(p, "cone") == 0)
+ g.kind = kMainViewGraphicCone;
+ else if (strcmp(p, "ellipsoid") == 0)
+ g.kind = kMainViewGraphicEllipsoid;
+ else rb_raise(rb_eMolbyError, "unknown graphic object type: %s", p);
+ }
g.closed = (RTEST(fval) ? 1 : 0);
cval = rb_ary_to_ary(cval);
n = RARRAY_LEN(cval);
NewArray(&g.points, &g.npoints, sizeof(GLfloat) * 3, n);
for (i = 0; i < n; i++) {
Vector v;
+ VALUE rval = RARRAY_PTR(pval)[i];
if (i == ni) {
- v.x = NUM2DBL(rb_Float(RARRAY_PTR(pval)[i]));
+ if (rb_obj_is_kind_of(rval, rb_cVector3D)) {
+ /* The float argument can also be given as a vector (for simplify undo registration) */
+ VectorFromValue(rval, &v);
+ } else {
+ v.x = NUM2DBL(rb_Float(rval));
+ }
v.y = v.z = 0;
} else {
- VectorFromValue(RARRAY_PTR(pval)[i], &v);
+ VectorFromValue(rval, &v);
}
g.points[i * 3] = v.x;
g.points[i * 3 + 1] = v.y;
g.points[6] = g.points[8] = g.points[9] = g.points[10] = 0;
g.points[7] = g.points[11] = g.points[3];
}
- MainView_insertGraphic(mol->mview, -1, &g);
- return INT2NUM(mol->mview->ngraphics - 1);
+ if (g.kind == kMainViewGraphicPoly) {
+ /* Calculate normals */
+ s_CalculateGraphicNormals(&g);
+ }
+ MainView_insertGraphic(mol->mview, idx, &g);
+
+ {
+ /* Register undo */
+ MolAction *act;
+ act = MolActionNew(SCRIPT_ACTION("i"), "remove_graphic", idx);
+ MolActionCallback_registerUndo(mol, act);
+ MolActionRelease(act);
+ }
+
+ return INT2NUM(idx);
+}
+
+/*
+ * call-seq:
+ * create_graphic(kind, color, points, fill = nil) -> integer
+ *
+ * Create a new graphic object. The arguments are similar as insert_graphic.
+ */
+static VALUE
+s_Molecule_CreateGraphic(int argc, VALUE *argv, VALUE self)
+{
+ VALUE args[5];
+ rb_scan_args(argc, argv, "31", args + 1, args + 2, args + 3, args + 4);
+ args[0] = INT2NUM(-1);
+ return s_Molecule_InsertGraphic(argc + 1, args, self);
}
/*
i = NUM2INT(rb_Integer(ival));
if (i < 0 || i >= mol->mview->ngraphics)
rb_raise(rb_eArgError, "graphic index is out of range");
+ {
+ /* Prepare data for undo */
+ MainViewGraphic *gp;
+ Vector *vp;
+ MolAction *act;
+ double col[4];
+ int n;
+ gp = mol->mview->graphics + i;
+ vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
+ for (n = 0; n < gp->npoints; n++) {
+ vp[n].x = gp->points[n * 3];
+ vp[n].y = gp->points[n * 3 + 1];
+ vp[n].z = gp->points[n * 3 + 2];
+ }
+ col[0] = gp->rgba[0];
+ col[1] = gp->rgba[1];
+ col[2] = gp->rgba[2];
+ col[3] = gp->rgba[3];
+ if (gp->visible == 0) {
+ act = MolActionNew(SCRIPT_ACTION("i"), "hide_graphic", i);
+ MolActionCallback_registerUndo(mol, act);
+ MolActionRelease(act);
+ }
+ act = MolActionNew(SCRIPT_ACTION("iiDVb"), "insert_graphic", i, gp->kind, 4, col, gp->npoints, vp, gp->closed);
+ MolActionCallback_registerUndo(mol, act);
+ free(vp);
+ MolActionRelease(act);
+ }
MainView_removeGraphic(mol->mview, i);
return ival;
}
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
- return INT2NUM(mol->mview->ngraphics);
+ return INT2NUM(mol->mview->ngraphics);
+}
+
+/*
+ * call-seq:
+ * get_graphic_point(graphic_index, point_index) -> value
+ * get_graphic_points(graphic_index) -> values
+ *
+ * Get the point_index-th control point of graphic_index-th graphic object.
+ * Get an array of all control points with the given values.
+ *
+ */
+static VALUE
+s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
+{
+ MainViewGraphic *gp;
+ Molecule *mol;
+ int index, pindex;
+ Vector v;
+ VALUE gval, pval;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
+ rb_scan_args(argc, argv, "11", &gval, &pval);
+ index = NUM2INT(rb_Integer(gval));
+ if (index < 0 || index >= mol->mview->ngraphics)
+ rb_raise(rb_eArgError, "the graphic index is out of range");
+ gp = mol->mview->graphics + index;
+ if (pval != Qnil) {
+ pindex = NUM2INT(rb_Integer(pval));
+ if (pindex < 0 || pindex >= gp->npoints)
+ rb_raise(rb_eArgError, "the point index is out of range");
+ v.x = gp->points[pindex * 3];
+ v.y = gp->points[pindex * 3 + 1];
+ v.z = gp->points[pindex * 3 + 2];
+ if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
+ return rb_float_new(v.x);
+ } else {
+ return ValueFromVector(&v);
+ }
+ } else {
+ pval = rb_ary_new();
+ for (pindex = 0; pindex < gp->npoints; pindex++) {
+ v.x = gp->points[pindex * 3];
+ v.y = gp->points[pindex * 3 + 1];
+ v.z = gp->points[pindex * 3 + 2];
+ rb_ary_push(pval, ValueFromVector(&v));
+ }
+ return pval;
+ }
+}
+
+/*
+ * call-seq:
+ * set_graphic_point(graphic_index, point_index, new_value) -> new_value
+ * set_graphic_points(graphic_index, new_values) -> new_values
+ *
+ * Change the point_index-th control point of graphic_index-th graphic object.
+ * Replace the control points with the given values.
+ *
+ */
+static VALUE
+s_Molecule_SetGraphicPoint(int argc, VALUE *argv, VALUE self)
+{
+ MainViewGraphic *gp;
+ Molecule *mol;
+ int index, pindex;
+ Vector v, v0;
+ VALUE gval, pval, nval;
+ MolAction *act;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mview == NULL)
+ rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
+ rb_scan_args(argc, argv, "21", &gval, &pval, &nval);
+ index = NUM2INT(rb_Integer(gval));
+ if (index < 0 || index >= mol->mview->ngraphics)
+ rb_raise(rb_eArgError, "the graphic index is out of range");
+ gp = mol->mview->graphics + index;
+ if (nval != Qnil) {
+ pindex = NUM2INT(rb_Integer(pval));
+ if (pindex < 0 || pindex >= gp->npoints)
+ rb_raise(rb_eArgError, "the point index is out of range");
+ v0.x = gp->points[pindex * 3];
+ v0.y = gp->points[pindex * 3 + 1];
+ v0.z = gp->points[pindex * 3 + 2];
+ if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
+ if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
+ v.x = NUM2DBL(rb_Float(nval));
+ v.y = v.z = 0;
+ } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1) {
+ v.x = NUM2DBL(rb_Float(nval));
+ v.y = v.z = 0;
+ gp->points[7] = gp->points[11] = v.x;
+ gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
+ } else rb_raise(rb_eArgError, "the argument must be an array-like object");
+ } else {
+ if (nval == Qnil) {
+ v.x = kInvalidFloat;
+ v.y = v.z = 0.0;
+ } else VectorFromValue(nval, &v);
+ }
+ gp->points[pindex * 3] = v.x;
+ gp->points[pindex * 3 + 1] = v.y;
+ gp->points[pindex * 3 + 2] = v.z;
+ act = MolActionNew(SCRIPT_ACTION("iiv"), "set_graphic_point", index, pindex, &v0);
+ } else {
+ VALUE aval;
+ int len;
+ Vector *vp = (Vector *)malloc(sizeof(Vector) * gp->npoints);
+ for (pindex = 0; pindex < gp->npoints; pindex++) {
+ vp[pindex].x = gp->points[pindex * 3];
+ vp[pindex].y = gp->points[pindex * 3 + 1];
+ vp[pindex].z = gp->points[pindex * 3 + 2];
+ }
+ act = MolActionNew(SCRIPT_ACTION("iV"), "set_graphic_points", index, gp->npoints, vp);
+ free(vp);
+ pval = rb_ary_to_ary(pval);
+ len = RARRAY_LEN(pval);
+ if (gp->npoints < len) {
+ gp->points = (GLfloat *)realloc(gp->points, sizeof(GLfloat) * 3 * len);
+ gp->npoints = len;
+ } else if (gp->npoints > len) {
+ int len2 = 3;
+ switch (gp->kind) {
+ case kMainViewGraphicLine: len2 = 2; break;
+ case kMainViewGraphicPoly: len2 = 3; break;
+ case kMainViewGraphicCylinder: len2 = 3; break;
+ case kMainViewGraphicCone: len2 = 3; break;
+ case kMainViewGraphicEllipsoid: len2 = 4; break;
+ }
+ if (len2 < len)
+ len2 = len;
+ gp->npoints = len2;
+ }
+ for (pindex = 0; pindex < len && pindex < gp->npoints; pindex++) {
+ aval = RARRAY_PTR(pval)[pindex];
+ if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && pindex == 2) {
+ v.x = NUM2DBL(rb_Float(aval));
+ v.y = v.z = 0;
+ } else if (gp->kind == kMainViewGraphicEllipsoid && pindex == 1 && len == 2) {
+ v.x = NUM2DBL(rb_Float(aval));
+ v.y = v.z = 0;
+ gp->points[7] = gp->points[11] = v.x;
+ gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
+ break;
+ } else VectorFromValue(aval, &v);
+ gp->points[pindex * 3] = v.x;
+ gp->points[pindex * 3 + 1] = v.y;
+ gp->points[pindex * 3 + 2] = v.z;
+ }
+ }
+ if (gp->kind == kMainViewGraphicPoly) {
+ /* Calculate normals */
+ s_CalculateGraphicNormals(gp);
+ }
+ MolActionCallback_registerUndo(mol, act);
+ MolActionRelease(act);
+ MoleculeCallback_notifyModification(mol, 0);
+ return nval;
}
-
+
/*
* call-seq:
- * set_graphic_point(graphic_index, point_index, new_value) -> new_value
+ * get_graphic_color(graphic_index) -> value
*
- * Change the point_index-th control point of graphic_index-th graphic object
- *
+ * Get the color of graphic_index-th graphic object
*/
static VALUE
-s_Molecule_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
+s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
{
MainViewGraphic *gp;
Molecule *mol;
int index;
- Vector v;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
if (index < 0 || index >= mol->mview->ngraphics)
rb_raise(rb_eArgError, "the graphic index is out of range");
gp = mol->mview->graphics + index;
- index = NUM2INT(rb_Integer(pval));
- if (index < 0 || index >= gp->npoints)
- rb_raise(rb_eArgError, "the point index is out of range");
- if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
- if ((gp->kind == kMainViewGraphicCylinder || gp->kind == kMainViewGraphicCone) && index == 2) {
- v.x = NUM2DBL(rb_Float(nval));
- v.y = v.z = 0;
- } else if (gp->kind == kMainViewGraphicEllipsoid && index == 1) {
- gp->points[3] = gp->points[7] = gp->points[11] = NUM2DBL(rb_Float(nval));
- gp->points[4] = gp->points[5] = gp->points[6] = gp->points[8] = gp->points[9] = gp->points[10] = 0;
- return nval;
- } else rb_raise(rb_eArgError, "the argument must be an array-like object");
- } else {
- if (nval == Qnil) {
- v.x = kInvalidFloat;
- v.y = v.z = 0.0;
- } else VectorFromValue(nval, &v);
- }
- gp->points[index * 3] = v.x;
- gp->points[index * 3 + 1] = v.y;
- gp->points[index * 3 + 2] = v.z;
- MoleculeCallback_notifyModification(mol, 0);
- return nval;
+ return rb_ary_new3(4, rb_float_new(gp->rgba[0]), rb_float_new(gp->rgba[1]), rb_float_new(gp->rgba[2]), rb_float_new(gp->rgba[3]));
}
/*
{
MainViewGraphic *gp;
Molecule *mol;
- int index, n;
+ MolAction *act;
+ double c[4];
+ int index, i, n;
Data_Get_Struct(self, Molecule, mol);
if (mol->mview == NULL)
rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
if (index < 0 || index >= mol->mview->ngraphics)
rb_raise(rb_eArgError, "the graphic index is out of range");
gp = mol->mview->graphics + index;
+ for (i = 0; i < 4; i++)
+ c[i] = gp->rgba[i];
cval = rb_ary_to_ary(cval);
n = RARRAY_LEN(cval);
if (n != 3 && n != 4)
rb_raise(rb_eArgError, "the color argument must have 3 or 4 numbers");
- for (index = 0; index < n; index++) {
- gp->rgba[index] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[index]));
+
+ for (i = 0; i < n; i++) {
+ gp->rgba[i] = NUM2DBL(rb_Float(RARRAY_PTR(cval)[i]));
}
if (n == 3)
gp->rgba[3] = 1.0;
+ act = MolActionNew(SCRIPT_ACTION("iD"), "set_graphic_color", index, 4, c);
+ MolActionCallback_registerUndo(mol, act);
+ MolActionRelease(act);
MoleculeCallback_notifyModification(mol, 0);
return cval;
}
return Qnil;
}
+#pragma mark ------ MD Support ------
+
/*
* call-seq:
* md_arena -> MDArena
/*
* call-seq:
+ * start_step -> Integer
+ *
+ * Returns the start step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_StartStep(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ return INT2NUM(mol->startStep);
+}
+
+/*
+ * call-seq:
+ * start_step = Integer
+ *
+ * Set the start step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetStartStep(VALUE self, VALUE val)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ mol->startStep = NUM2INT(rb_Integer(val));
+ return val;
+}
+
+/*
+ * call-seq:
+ * steps_per_frame -> Integer
+ *
+ * Returns the number of steps between frames (defined by dcd format).
+ */
+static VALUE
+s_Molecule_StepsPerFrame(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ return INT2NUM(mol->stepsPerFrame);
+}
+
+/*
+ * call-seq:
+ * steps_per_frame = Integer
+ *
+ * Set the number of steps between frames (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetStepsPerFrame(VALUE self, VALUE val)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ mol->stepsPerFrame = NUM2INT(rb_Integer(val));
+ return val;
+}
+
+/*
+ * call-seq:
+ * ps_per_step -> Float
+ *
+ * Returns the time increment (in picoseconds) for one step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_PsPerStep(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ return rb_float_new(mol->psPerStep);
+}
+
+/*
+ * call-seq:
+ * ps_per_step = Float
+ *
+ * Set the time increment (in picoseconds) for one step (defined by dcd format).
+ */
+static VALUE
+s_Molecule_SetPsPerStep(VALUE self, VALUE val)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ mol->psPerStep = NUM2DBL(rb_Float(val));
+ return val;
+}
+
+static VALUE
+s_Molecule_BondParIsObsolete(VALUE self, VALUE val)
+{
+ 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.");
+}
+
+#pragma mark ------ MO Handling ------
+
+/*
+ * call-seq:
* selectedMO -> IntGroup
*
* Returns a group of selected mo in the "MO Info" table. If the MO info table
/* Set up parameters */
mono = NUM2INT(rb_Integer(mval));
- if (mono <= 0 || mono > mol->bset->ncomps)
- rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d)", mono, mol->bset->ncomps);
+ if (mono < 0 || mono > mol->bset->ncomps)
+ rb_raise(rb_eMolbyError, "The MO number (%d) is out of range (should be 1..%d, or 0 as 'arbitrary vector')", mono, mol->bset->ncomps);
if (RTEST(bval)) {
if (mol->bset->rflag != 0)
rb_raise(rb_eMolbyError, "Beta MO is requested but not present");
/*
* call-seq:
+ * clear_surface
+ *
+ * Clear the MO surface if present.
+ */
+static VALUE
+s_Molecule_ClearSurface(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mcube != NULL)
+ MoleculeClearMCube(mol, 0, 0, 0, NULL, 0.0, 0.0, 0.0);
+ return self;
+}
+
+/*
+ * call-seq:
+ * hide_surface
+ *
+ * Hide the MO surface if present.
+ */
+static VALUE
+s_Molecule_HideSurface(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mcube != NULL) {
+ mol->mcube->hidden = 1;
+ MoleculeCallback_notifyModification(mol, 0);
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * show_surface
+ *
+ * Show the MO surface if present.
+ */
+static VALUE
+s_Molecule_ShowSurface(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->mcube != NULL) {
+ mol->mcube->hidden = 0;
+ MoleculeCallback_notifyModification(mol, 0);
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * create_surface(mo, attr = nil)
+ *
+ * Create a MO surface. The argument mo is the MO index (1-based); if mo is negative,
+ * then it denotes the beta orbital.
+ * If mo is nil, then the attributes of the current surface are modified.
+ * Attributes:
+ * :npoints : the approximate number of grid points
+ * :expand : the scale factor to expand/shrink the display box size for each atom,
+ * :thres : the threshold for the isovalue surface
+ * If the molecule does not contain MO information, raises exception.
+ */
+static VALUE
+s_Molecule_CreateSurface(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ Vector o, dx, dy, dz;
+ Int nmo, nx, ny, nz, i;
+ Int need_recalc = 0;
+ VALUE nval, hval, aval;
+ Int npoints;
+ Double expand;
+ Double thres;
+ Double d[4];
+ Data_Get_Struct(self, Molecule, mol);
+ rb_scan_args(argc, argv, "11", &nval, &hval);
+ if (mol->bset == NULL)
+ rb_raise(rb_eMolbyError, "No MO information is given");
+ if (nval == Qnil) {
+ nmo = -1;
+ } else if (nval == ID2SYM(rb_intern("total_density"))) {
+ nmo = mol->bset->nmos + 1;
+ } else {
+ nmo = NUM2INT(rb_Integer(nval));
+ if (nmo > mol->bset->nmos || nmo < -mol->bset->ncomps)
+ rb_raise(rb_eMolbyError, "MO index (%d) is out of range; should be 1..%d (or -1..-%d for beta orbitals); (0 is acceptable as arbitrary vector)", nmo, mol->bset->nmos, mol->bset->ncomps);
+ if (nmo < 0)
+ nmo = -nmo + mol->bset->ncomps;
+ }
+ if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("npoints")))) != Qnil) {
+ npoints = NUM2INT(rb_Integer(aval));
+ need_recalc = 1;
+ } else if (mol->mcube != NULL) {
+ npoints = mol->mcube->nx * mol->mcube->ny * mol->mcube->nz;
+ } else npoints = 80 * 80 * 80;
+ if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("expand")))) != Qnil) {
+ expand = NUM2DBL(rb_Float(aval));
+ } else if (mol->mcube != NULL) {
+ expand = mol->mcube->expand;
+ } else expand = 1.0;
+ if (hval != Qnil && (aval = rb_hash_aref(hval, ID2SYM(rb_intern("thres")))) != Qnil) {
+ thres = NUM2DBL(rb_Float(aval));
+ } else if (mol->mcube != NULL) {
+ thres = mol->mcube->thres;
+ } else thres = 0.05;
+ if (mol->mcube == NULL || npoints != mol->mcube->nx * mol->mcube->ny * mol->mcube->nz) {
+ if (MoleculeGetDefaultMOGrid(mol, npoints, &o, &dx, &dy, &dz, &nx, &ny, &nz) != 0)
+ rb_raise(rb_eMolbyError, "Cannot get default grid size (internal error?)");
+ if (MoleculeClearMCube(mol, nx, ny, nz, &o, dx.x, dy.y, dz.z) == NULL)
+ rb_raise(rb_eMolbyError, "Cannot allocate memory for MO surface calculation");
+ }
+ for (nx = 0; nx < 2; nx++) {
+ aval = ID2SYM(rb_intern(nx == 0 ? "color0" : "color"));
+ if (hval != Qnil && (aval = rb_hash_aref(hval, aval)) != Qnil) {
+ aval = rb_ary_to_ary(aval);
+ if (RARRAY_LEN(aval) < 3) {
+ raise:
+ rb_raise(rb_eMolbyError, "The color%s value must be an array with at least 3 float numbers", (nx == 0 ? "0" : ""));
+ }
+ for (i = 0; i < 4; i++)
+ d[i] = mol->mcube->c[nx].rgba[i];
+ for (i = 0; i < 4 && i < RARRAY_LEN(aval); i++) {
+ d[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
+ if (d[i] < 0.0 && d[i] > 1.0)
+ goto raise;
+ }
+ for (i = 0; i < 4; i++)
+ mol->mcube->c[nx].rgba[i] = d[i];
+ }
+ }
+ if (mol->mcube->expand != expand)
+ need_recalc = 1;
+ mol->mcube->thres = thres;
+ mol->mcube->expand = expand;
+ if (nmo < 0) {
+ if (mol->mcube->idn < 0)
+ return self; /* Only set attributes for now */
+ if (need_recalc)
+ nmo = mol->mcube->idn; /* Force recalculation */
+ }
+ if (MoleculeUpdateMCube(mol, nmo) != 0)
+ rb_raise(rb_eMolbyError, "Cannot complete MO surface calculation");
+ return self;
+}
+
+/*
+ * call-seq:
+ * set_surface_attr(attr = nil)
+ *
+ * Set the drawing attributes of the surface. Attr is a hash containing the attributes.
+ */
+static VALUE
+s_Molecule_SetSurfaceAttr(VALUE self, VALUE hval)
+{
+ VALUE args[2];
+ args[0] = Qnil;
+ args[1] = hval;
+ return s_Molecule_CreateSurface(2, args, self);
+}
+
+/*
+ * call-seq:
* nelpots
*
* Get the number of electrostatic potential info.
/*
* call-seq:
- * add_gaussian_orbital_shell(sym, nprims, atom_index)
+ * clear_basis_set
+ *
+ * Clear the existing basis set info. All gaussian coefficients, MO energies and coefficients,
+ * cube and marching cube information are discarded. This operation is _not_ undoable!
+ */
+static VALUE
+s_Molecule_ClearBasisSet(VALUE self)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol != NULL) {
+ if (mol->bset != NULL) {
+ BasisSetRelease(mol->bset);
+ mol->bset = NULL;
+ }
+ if (mol->mcube != NULL) {
+ MoleculeDeallocateMCube(mol->mcube);
+ mol->mcube = NULL;
+ }
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * add_gaussian_orbital_shell(atom_index, sym, no_of_primitives)
*
- * To be used internally. Add a gaussian orbital shell with symmetry code, number of primitives,
- * and the corresponding atom index. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
+ * To be used internally. Add a gaussian orbital shell with the atom index, symmetry code,
+ * and the number of primitives. Symmetry code: 0, S-type; 1, P-type; -1, SP-type; 2, D-type;
* -2, D5-type.
*/
static VALUE
-s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE symval, VALUE npval, VALUE aval)
+s_Molecule_AddGaussianOrbitalShell(VALUE self, VALUE aval, VALUE symval, VALUE npval)
{
Molecule *mol;
int sym, nprims, a_idx, n;
Data_Get_Struct(self, Molecule, mol);
+ a_idx = NUM2INT(rb_Integer(aval));
sym = NUM2INT(rb_Integer(symval));
nprims = NUM2INT(rb_Integer(npval));
- a_idx = NUM2INT(rb_Integer(aval));
- n = MoleculeAddGaussianOrbitalShell(mol, sym, nprims, a_idx);
+ n = MoleculeAddGaussianOrbitalShell(mol, a_idx, sym, nprims);
if (n == -1)
rb_raise(rb_eMolbyError, "Molecule is emptry");
else if (n == -2)
/*
* call-seq:
- * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
+ * add_gaussian_primitive_coefficients(exponent, contraction, contraction_sp)
+ *
+ * To be used internally. Add a gaussian primitive coefficients.
+ */
+static VALUE
+s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
+{
+ Molecule *mol;
+ Int n;
+ Double exponent, contraction, contraction_sp;
+ Data_Get_Struct(self, Molecule, mol);
+ exponent = NUM2DBL(rb_Float(expval));
+ contraction = NUM2DBL(rb_Float(cval));
+ contraction_sp = NUM2DBL(rb_Float(cspval));
+ n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
+ if (n == -1)
+ rb_raise(rb_eMolbyError, "Molecule is emptry");
+ else if (n == -2)
+ rb_raise(rb_eMolbyError, "Low memory");
+ else if (n != 0)
+ rb_raise(rb_eMolbyError, "Unknown error");
+ return self;
+}
+
+/*
+ * call-seq:
+ * get_gaussian_shell_info(shell_index) -> [atom_index, sym, no_of_primitives, comp_index, no_of_components]
+ *
+ * Get the Gaussian shell information for the given MO coefficient index.
+ * The symmetry code is the same as in add_gaussian_orbital_shell.
+ * The comp_index is the index of the first MO component belonging to this shell, and no_of_components
+ * is the number of MO component belonging to this shell.
+ */
+static VALUE
+s_Molecule_GetGaussianShellInfo(VALUE self, VALUE sval)
+{
+ Molecule *mol;
+ ShellInfo *sp;
+ int s_idx, sym;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->bset == NULL)
+ rb_raise(rb_eMolbyError, "No basis set information is defined");
+ s_idx = NUM2INT(rb_Integer(sval));
+ if (s_idx < 0 || s_idx >= mol->bset->nshells)
+ return Qnil;
+ sp = mol->bset->shells + s_idx;
+ sym = sp->sym;
+ switch (sym) {
+ case kGTOType_S: sym = 0; break;
+ case kGTOType_SP: sym = -1; break;
+ case kGTOType_P: sym = 1; break;
+ case kGTOType_D: sym = 2; break;
+ case kGTOType_D5: sym = -2; break;
+ case kGTOType_F: sym = 3; break;
+ case kGTOType_F7: sym = -3; break;
+ case kGTOType_G: sym = 4; break;
+ case kGTOType_G9: sym = -4; break;
+ default:
+ rb_raise(rb_eMolbyError, "The Gaussian shell type (%d) is unknown (internal error?)", sym);
+ }
+ return rb_ary_new3(5, INT2NUM(sp->a_idx), INT2NUM(sym), INT2NUM(sp->nprim), INT2NUM(sp->m_idx), INT2NUM(sp->ncomp));
+}
+
+/*
+ * call-seq:
+ * get_gaussian_primitive_coefficients(shell_index) -> [[exp1, con1 [, con_sp1]], [exp2, con2 [, con_sp2]],...]
+ *
+ * Get the Gaussian primitive coefficients for the given MO component.
+ */
+static VALUE
+s_Molecule_GetGaussianPrimitiveCoefficients(VALUE self, VALUE sval)
+{
+ Molecule *mol;
+ ShellInfo *sp;
+ PrimInfo *pp;
+ int s_idx, i;
+ VALUE retval, aval;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->bset == NULL)
+ rb_raise(rb_eMolbyError, "No basis set information is defined");
+ s_idx = NUM2INT(rb_Integer(sval));
+ if (s_idx < 0 || s_idx >= mol->bset->nshells)
+ return Qnil;
+ sp = mol->bset->shells + s_idx;
+ pp = mol->bset->priminfos + sp->p_idx;
+ retval = rb_ary_new2(sp->nprim);
+ for (i = 0; i < sp->nprim; i++) {
+ if (sp->sym == kGTOType_SP) {
+ /* With P contraction coefficient */
+ aval = rb_ary_new3(3, rb_float_new(pp[i].A), rb_float_new(pp[i].C), rb_float_new(pp[i].Csp));
+ } else {
+ /* Without P contraction coefficient */
+ aval = rb_ary_new3(2, rb_float_new(pp[i].A), rb_float_new(pp[i].C));
+ }
+ rb_ary_store(retval, i, aval);
+ }
+ return retval;
+}
+
+/*
+ * call-seq:
+ * get_gaussian_component_info(comp_index) -> [atom_index, shell_index, orbital_description]
*
- * To be used internally. Add a gaussian primitive coefficients.
+ * Get the Gaussian shell information for the given MO coefficient index.
*/
static VALUE
-s_Molecule_AddGaussianPrimitiveCoefficients(VALUE self, VALUE expval, VALUE cval, VALUE cspval)
+s_Molecule_GetGaussianComponentInfo(VALUE self, VALUE cval)
{
Molecule *mol;
- Int n;
- Double exponent, contraction, contraction_sp;
+ Int n, c, atom_idx, shell_idx;
+ char label[32];
Data_Get_Struct(self, Molecule, mol);
- exponent = NUM2DBL(rb_Float(expval));
- contraction = NUM2DBL(rb_Float(cval));
- contraction_sp = NUM2DBL(rb_Float(cspval));
- n = MoleculeAddGaussianPrimitiveCoefficients(mol, exponent, contraction, contraction_sp);
- if (n == -1)
- rb_raise(rb_eMolbyError, "Molecule is emptry");
- else if (n == -2)
- rb_raise(rb_eMolbyError, "Low memory");
- else if (n != 0)
- rb_raise(rb_eMolbyError, "Unknown error");
- return self;
+ if (mol->bset == NULL)
+ rb_raise(rb_eMolbyError, "No basis set information is defined");
+ c = NUM2INT(rb_Integer(cval));
+ if (c < 0 || c >= mol->bset->ncomps)
+ return Qnil;
+ n = MoleculeGetGaussianComponentInfo(mol, c, &atom_idx, label, &shell_idx);
+ if (n != 0)
+ rb_raise(rb_eMolbyError, "Cannot get the shell info for component index (%d)", c);
+ return rb_ary_new3(3, INT2NUM(atom_idx), INT2NUM(shell_idx), Ruby_NewEncodedStringValue2(label));
}
/*
* call-seq:
- * mo_type
+ * clear_mo_coefficients
*
- * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
+ * Clear the existing MO coefficients.
*/
static VALUE
-s_Molecule_MOType(VALUE self)
+s_Molecule_ClearMOCoefficients(VALUE self)
{
Molecule *mol;
- Data_Get_Struct(self, Molecule, mol);
- if (mol != NULL && mol->bset != NULL) {
- const char *s;
- int rflag = mol->bset->rflag;
- if (rflag == 0)
- s = "UHF";
- else if (rflag == 2)
- s = "ROHF";
- else s = "RHF";
- return rb_str_new2(s);
- } else return Qnil;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->bset != NULL) {
+ if (mol->bset->moenergies != NULL) {
+ free(mol->bset->moenergies);
+ mol->bset->moenergies = NULL;
+ }
+ if (mol->bset->mo != NULL) {
+ free(mol->bset->mo);
+ mol->bset->mo = NULL;
+ }
+ mol->bset->nmos = 0;
+ }
+ return self;
}
/*
* call-seq:
* set_mo_coefficients(idx, energy, coefficients)
*
- * To be used internally. Add a MO coefficients. Idx is the MO index (for open shell system,
- * beta MOs comes after all alpha MOs), energy is the MO energy, coefficients is an array
+ * To be used internally. Add a MO coefficients. Idx is the MO index (1-based; for open shell system,
+ * beta MOs comes after all alpha MOs; alternatively, the beta MO can be specified as a negative MO number)
+ * Energy is the MO energy, and coefficients is an array
* of MO coefficients.
*/
static VALUE
}
for (i = 0; i < ncomps; i++)
coeffs[i] = NUM2DBL(rb_Float(RARRAY_PTR(aval)[i]));
- i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs);
+ i = MoleculeSetMOCoefficients(mol, idx, energy, ncomps, coeffs); /* Negative (beta orbitals) or zero (arbitrary vector) idx is allowed */
end:
if (i == -1)
rb_raise(rb_eMolbyError, "Molecule is emptry");
else if (i == -3)
rb_raise(rb_eMolbyError, "Bad or inconsistent number of MOs");
else if (i == -4)
- rb_raise(rb_eMolbyError, "Bad MO index");
+ rb_raise(rb_eMolbyError, "Bad MO index (%d)", idx);
else if (i == -5)
rb_raise(rb_eMolbyError, "Insufficient number of coefficients are given");
else if (i != 0)
/*
* call-seq:
- * allocate_basis_set_record(rflag, ne_alpha, ne_beta)
+ * get_mo_coefficients(idx)
*
- * To be used internally. Allocate a basis set record. rflag: 0, unrestricted; 1, restricted.
- * ne_alpha, ne_beta: number of alpha/beta electrons.
+ * To be used internally. Get an array of MO coefficients for the given MO index (1-based).
*/
static VALUE
-s_Molecule_AllocateBasisSetRecord(VALUE self, VALUE rval, VALUE naval, VALUE nbval)
+s_Molecule_GetMOCoefficients(VALUE self, VALUE ival)
{
Molecule *mol;
- Int rflag, na, nb, n;
+ Int idx, ncomps, n;
+ Double energy;
+ Double *coeffs;
+ VALUE retval;
Data_Get_Struct(self, Molecule, mol);
- rflag = NUM2INT(rb_Integer(rval));
- na = NUM2INT(rb_Integer(naval));
- nb = NUM2INT(rb_Integer(nbval));
- n = MoleculeAllocateBasisSetRecord(mol, rflag, na, nb);
+ idx = NUM2INT(rb_Integer(ival));
+ ncomps = 0;
+ coeffs = NULL;
+ n = MoleculeGetMOCoefficients(mol, idx, &energy, &ncomps, &coeffs);
if (n == -1)
rb_raise(rb_eMolbyError, "Molecule is emptry");
else if (n == -2)
- rb_raise(rb_eMolbyError, "Low memory");
- else if (n != 0)
- rb_raise(rb_eMolbyError, "Unknown error");
+ rb_raise(rb_eMolbyError, "No basis set information is present");
+ else if (n == -3)
+ return Qnil; /* Silently returns nil */
+ retval = rb_ary_new2(ncomps);
+ for (n = 0; n < ncomps; n++)
+ rb_ary_store(retval, n, rb_float_new(coeffs[n]));
+ free(coeffs);
+ return retval;
+}
+
+/*
+ * call-seq:
+ * get_mo_energy(idx)
+ *
+ * To be used internally. Get the MO energy for the given MO index (1-based).
+ */
+static VALUE
+s_Molecule_GetMOEnergy(VALUE self, VALUE ival)
+{
+ Molecule *mol;
+ Int idx, n;
+ Double energy;
+ Data_Get_Struct(self, Molecule, mol);
+ idx = NUM2INT(rb_Integer(ival));
+ n = MoleculeGetMOCoefficients(mol, idx, &energy, NULL, NULL);
+ if (n == -1)
+ rb_raise(rb_eMolbyError, "Molecule is emptry");
+ else if (n == -2)
+ rb_raise(rb_eMolbyError, "No basis set information is present");
+ else if (n == -3)
+ return Qnil;
+ return rb_float_new(energy);
+}
+
+static VALUE sTypeSym, sAlphaSym, sBetaSym, sNcompsSym, sNshellsSym;
+
+static inline void
+s_InitMOInfoKeys(void)
+{
+ if (sTypeSym == 0) {
+ sTypeSym = ID2SYM(rb_intern("type"));
+ sAlphaSym = ID2SYM(rb_intern("alpha"));
+ sBetaSym = ID2SYM(rb_intern("beta"));
+ sNcompsSym = ID2SYM(rb_intern("ncomps"));
+ sNshellsSym = ID2SYM(rb_intern("nshells"));
+ }
+}
+
+/*
+ * call-seq:
+ * set_mo_info(hash)
+ *
+ * Set the MO info. hash keys: :type=>"RHF"|"UHF"|"ROHF",
+ * :alpha=>integer, :beta=>integer
+ */
+static VALUE
+s_Molecule_SetMOInfo(VALUE self, VALUE hval)
+{
+ Molecule *mol;
+ VALUE aval;
+ Int rflag, na, nb, n;
+ char *s;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->bset != NULL) {
+ rflag = mol->bset->rflag;
+ na = mol->bset->ne_alpha;
+ nb = mol->bset->ne_beta;
+ } else {
+ rflag = 1;
+ na = 0;
+ nb = 0;
+ }
+ if (hval != Qnil) {
+ if ((aval = rb_hash_aref(hval, sTypeSym)) != Qnil) {
+ s = StringValuePtr(aval);
+ if (strcasecmp(s, "RHF") == 0)
+ rflag = 1;
+ else if (strcasecmp(s, "UHF") == 0)
+ rflag = 0;
+ else if (strcasecmp(s, "ROHF") == 0)
+ rflag = 2;
+ }
+ if ((aval = rb_hash_aref(hval, sAlphaSym)) != Qnil) {
+ n = NUM2INT(rb_Integer(aval));
+ if (n >= 0)
+ na = n;
+ }
+ if ((aval = rb_hash_aref(hval, sBetaSym)) != Qnil) {
+ n = NUM2INT(rb_Integer(aval));
+ if (n >= 0)
+ nb = n;
+ }
+ MoleculeSetMOInfo(mol, rflag, na, nb);
+ }
return self;
}
/*
* call-seq:
+ * get_mo_info(key)
+ *
+ * Get the MO info. The key is as described in set_mo_info.
+ * Read-only: :ncomps the number of components (and the number of MOs), :nshells the number of shells
+ */
+static VALUE
+s_Molecule_GetMOInfo(VALUE self, VALUE kval)
+{
+ Molecule *mol;
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->bset == NULL)
+ return Qnil;
+ if (kval == sTypeSym) {
+ switch (mol->bset->rflag) {
+ case 0: return Ruby_NewEncodedStringValue2("UHF");
+ case 1: return Ruby_NewEncodedStringValue2("RHF");
+ case 2: return Ruby_NewEncodedStringValue2("ROHF");
+ default: return rb_str_to_str(INT2NUM(mol->bset->rflag));
+ }
+ } else if (kval == sAlphaSym) {
+ return INT2NUM(mol->bset->ne_alpha);
+ } else if (kval == sBetaSym) {
+ return INT2NUM(mol->bset->ne_beta);
+ } else if (kval == sNcompsSym) {
+ return INT2NUM(mol->bset->ncomps);
+ } else if (kval == sNshellsSym) {
+ return INT2NUM(mol->bset->nshells);
+ } else {
+ kval = rb_inspect(kval);
+ rb_raise(rb_eMolbyError, "Unknown MO info key: %s", StringValuePtr(kval));
+ return Qnil; /* Does not reach here */
+ }
+}
+
+/*
+ * call-seq:
+ * mo_type
+ *
+ * Returns either "RHF", "UHF", or "ROHF". If no MO info is present, returns nil.
+ */
+static VALUE
+s_Molecule_MOType(VALUE self)
+{
+ return s_Molecule_GetMOInfo(self, sTypeSym);
+}
+
+#pragma mark ------ Molecular Topology ------
+
+/*
+ * call-seq:
* search_equivalent_atoms(ig = nil)
*
* Search equivalent atoms (within the atom group if given). Returns an array of integers.
return Qnil;
rb_scan_args(argc, argv, "01", &val);
if (val != Qnil)
- ig = IntGroupFromValue(val);
+ ig = s_Molecule_AtomGroupFromValue(self, val);
else ig = NULL;
result = MoleculeSearchEquivalentAtoms(mol, ig);
if (result == NULL)
gval = *argv++;
argc -= 2;
Data_Get_Struct(self, Molecule, mol);
- ig = IntGroupFromValue(gval);
+ if (gval == Qnil)
+ ig = NULL;
+ else
+ ig = s_Molecule_AtomGroupFromValue(self, gval);
+ if (ig == NULL || IntGroupGetCount(ig) == 0)
+ rb_raise(rb_eMolbyError, "atom group is not given correctly");
memset(&a, 0, sizeof(a));
memset(&an, 0, sizeof(an));
strncpy(a.aname, StringValuePtr(nval), 4);
return Data_Wrap_Struct(rb_cAtomRef, 0, (void (*)(void *))AtomRefRelease, aref);
}
+#pragma mark ------ Molecular Properties ------
+
+/*
+ * call-seq:
+ * set_property(name, value[, index]) -> value
+ * set_property(name, values, group) -> values
+ *
+ * Set molecular property. A property is a floating-point number with a specified name,
+ * and can be set for each frame separately. The name of the property is given as a String.
+ * The value can be a single floating point number, which is set to the current frame.
+ *
+ */
+static VALUE
+s_Molecule_SetProperty(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ VALUE nval, vval, ival;
+ char *name;
+ IntGroup *ig;
+ Int i, n, idx, fidx;
+ Double *dp;
+ rb_scan_args(argc, argv, "21", &nval, &vval, &ival);
+ Data_Get_Struct(self, Molecule, mol);
+ if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
+ idx = NUM2INT(rb_Integer(nval));
+ if (idx < 0 || idx >= mol->nmolprops)
+ rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
+ } else {
+ name = StringValuePtr(nval);
+ idx = MoleculeLookUpProperty(mol, name);
+ if (idx < 0) {
+ idx = MoleculeCreateProperty(mol, name);
+ if (idx < 0)
+ rb_raise(rb_eMolbyError, "Cannot create molecular property %s", name);
+ }
+ }
+ if (rb_obj_is_kind_of(vval, rb_cNumeric)) {
+ if (ival == Qnil)
+ fidx = mol->cframe;
+ else {
+ fidx = NUM2INT(rb_Integer(ival));
+ n = MoleculeGetNumberOfFrames(mol);
+ if (fidx < 0 || fidx >= n)
+ rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
+ }
+ ig = IntGroupNewWithPoints(fidx, 1, -1);
+ dp = (Double *)malloc(sizeof(Double));
+ *dp = NUM2DBL(rb_Float(vval));
+ n = 1;
+ } else {
+ vval = rb_ary_to_ary(vval);
+ ig = IntGroupFromValue(ival);
+ n = IntGroupGetCount(ig);
+ if (n == 0)
+ rb_raise(rb_eMolbyError, "No frames are specified");
+ if (RARRAY_LEN(vval) < n)
+ rb_raise(rb_eMolbyError, "Values are missing; at least %d values should be given", n);
+ dp = (Double *)calloc(sizeof(Double), n);
+ for (i = 0; i < n; i++)
+ dp[i] = NUM2DBL(rb_Float(RARRAY_PTR(vval)[i]));
+ }
+
+ MolActionCreateAndPerform(mol, gMolActionSetProperty, idx, ig, n, dp);
+ free(dp);
+ IntGroupRelease(ig);
+ return self;
+}
+
+/*
+ * call-seq:
+ * get_property(name[, index]) -> value
+ * get_property(name, group) -> values
+ *
+ * Get molecular property. In the first form, a property value for a single frame is returned.
+ * (If index is omitted, then the value for the current frame is given)
+ * In the second form, an array of property values for the given frames is returned.
+ * If name is not one of known properties or a valid index integer, exception is raised.
+ */
+static VALUE
+s_Molecule_GetProperty(int argc, VALUE *argv, VALUE self)
+{
+ Molecule *mol;
+ VALUE nval, ival;
+ char *name;
+ IntGroup *ig;
+ Int i, n, idx, fidx;
+ Double *dp;
+ rb_scan_args(argc, argv, "11", &nval, &ival);
+ Data_Get_Struct(self, Molecule, mol);
+ if (mol->nmolprops == 0)
+ rb_raise(rb_eMolbyError, "The molecule has no properties");
+ if (rb_obj_is_kind_of(nval, rb_cNumeric)) {
+ idx = NUM2INT(rb_Integer(nval));
+ if (idx < 0 || idx >= mol->nmolprops)
+ rb_raise(rb_eMolbyError, "The property index (%d) is out of range; should be 0..%d", idx, mol->nmolprops - 1);
+ } else {
+ name = StringValuePtr(nval);
+ idx = MoleculeLookUpProperty(mol, name);
+ if (idx < 0)
+ rb_raise(rb_eMolbyError, "The molecule has no property '%s'", name);
+ }
+ if (rb_obj_is_kind_of(ival, rb_cNumeric) || ival == Qnil) {
+ if (ival == Qnil)
+ fidx = mol->cframe;
+ else {
+ fidx = NUM2INT(rb_Integer(ival));
+ n = MoleculeGetNumberOfFrames(mol);
+ if (fidx < 0 || fidx >= n)
+ rb_raise(rb_eMolbyError, "The frame index (%d) is out of range; should be 0..%d", fidx, n - 1);
+ }
+ ig = IntGroupNewWithPoints(fidx, 1, -1);
+ ival = INT2FIX(fidx);
+ n = 1;
+ } else {
+ ig = IntGroupFromValue(ival);
+ n = IntGroupGetCount(ig);
+ if (n == 0)
+ return rb_ary_new();
+ }
+ dp = (Double *)calloc(sizeof(Double), n);
+ MoleculeGetProperty(mol, idx, ig, dp);
+ if (FIXNUM_P(ival))
+ ival = rb_float_new(dp[0]);
+ else {
+ ival = rb_ary_new();
+ for (i = n - 1; i >= 0; i--) {
+ nval = rb_float_new(dp[i]);
+ rb_ary_store(ival, i, nval);
+ }
+ }
+ free(dp);
+ IntGroupRelease(ig);
+ return ival;
+}
+
+/*
+ * call-seq:
+ * property_names -> Array
+ *
+ * Get an array of property names.
+ */
+static VALUE
+s_Molecule_PropertyNames(VALUE self)
+{
+ Molecule *mol;
+ VALUE rval, nval;
+ int i;
+ Data_Get_Struct(self, Molecule, mol);
+ rval = rb_ary_new();
+ for (i = mol->nmolprops - 1; i >= 0; i--) {
+ nval = Ruby_NewEncodedStringValue2(mol->molprops[i].propname);
+ rb_ary_store(rval, i, nval);
+ }
+ return rval;
+}
+
+#pragma mark ------ Class methods ------
+
/*
* call-seq:
* current -> Molecule
for (idx = 0; (mol = MoleculeCallback_moleculeAtIndex(idx)) != NULL; idx++) {
VALUE name;
MoleculeCallback_displayName(mol, buf, sizeof buf);
- name = rb_str_new2(buf);
+ name = Ruby_NewEncodedStringValue2(buf);
if (rb_reg_match(val, name) != Qnil && --k == 0)
break;
}
Molecule *mol;
int i;
VALUE ary;
- i = 0;
- ary = rb_ary_new();
- while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
- rb_ary_push(ary, ValueFromMolecule(mol));
- i++;
- }
- return ary;
-}
-
-/*
- * call-seq:
- * error_message -> String
- *
- * Get the error_message from the last load/save method. If no error, returns nil.
- */
-static VALUE
-s_Molecule_ErrorMessage(VALUE klass)
-{
- if (gLoadSaveErrorMessage == NULL)
- return Qnil;
- else return rb_str_new2(gLoadSaveErrorMessage);
-}
-
-/*
- * call-seq:
- * set_error_message(String)
- * Molecule.error_message = String
- *
- * Get the error_message from the last load/save method. If no error, returns nil.
- */
-static VALUE
-s_Molecule_SetErrorMessage(VALUE klass, VALUE sval)
-{
- if (gLoadSaveErrorMessage != NULL) {
- free(gLoadSaveErrorMessage);
- gLoadSaveErrorMessage = NULL;
- }
- if (sval != Qnil) {
- sval = rb_str_to_str(sval);
- gLoadSaveErrorMessage = strdup(StringValuePtr(sval));
- }
- return sval;
-}
-
-/*
- * call-seq:
- * self == Molecule -> boolean
- *
- * True if the two arguments point to the same molecule.
- */
-static VALUE
-s_Molecule_Equal(VALUE self, VALUE val)
-{
- if (rb_obj_is_kind_of(val, rb_cMolecule)) {
- Molecule *mol1, *mol2;
- Data_Get_Struct(self, Molecule, mol1);
- Data_Get_Struct(val, Molecule, mol2);
- return (mol1 == mol2 ? Qtrue : Qfalse);
- } else return Qfalse;
+ i = 0;
+ ary = rb_ary_new();
+ while ((mol = MoleculeCallback_moleculeAtOrderedIndex(i)) != NULL) {
+ rb_ary_push(ary, ValueFromMolecule(mol));
+ i++;
+ }
+ return ary;
}
+#pragma mark ------ Call Subprocess ------
+
/* The callback functions for call_subprocess_async */
static int
s_Molecule_CallSubProcessAsync_EndCallback(Molecule *mol, int status)
return INT2NUM(n);
}
+#pragma mark ====== Define Molby Classes ======
+
void
Init_Molby(void)
{
/* class Molecule */
rb_cMolecule = rb_define_class_under(rb_mMolby, "Molecule", rb_cObject);
+
rb_define_alloc_func(rb_cMolecule, s_Molecule_Alloc);
rb_define_private_method(rb_cMolecule, "initialize", s_Molecule_Initialize, -1);
rb_define_private_method(rb_cMolecule, "initialize_copy", s_Molecule_InitCopy, 1);
+ rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
+ rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
+
rb_define_method(rb_cMolecule, "loadmbsf", s_Molecule_Loadmbsf, -1);
rb_define_method(rb_cMolecule, "loadpsf", s_Molecule_Loadpsf, -1);
rb_define_alias(rb_cMolecule, "loadpsfx", "loadpsf");
rb_define_method(rb_cMolecule, "loadfchk", s_Molecule_Loadfchk, -1);
rb_define_alias(rb_cMolecule, "loadfch", "loadfchk");
rb_define_method(rb_cMolecule, "loaddat", s_Molecule_Loaddat, -1);
- rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
- rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
rb_define_method(rb_cMolecule, "savembsf", s_Molecule_Savembsf, 1);
rb_define_method(rb_cMolecule, "savepsf", s_Molecule_Savepsf, 1);
rb_define_alias(rb_cMolecule, "savepsfx", "savepsf");
rb_define_method(rb_cMolecule, "savepdb", s_Molecule_Savepdb, 1);
rb_define_method(rb_cMolecule, "savedcd", s_Molecule_Savedcd, 1);
-/* rb_define_method(rb_cMolecule, "savetep", s_Molecule_Savetep, 1); */
+ rb_define_method(rb_cMolecule, "molload", s_Molecule_Load, -1);
+ rb_define_method(rb_cMolecule, "molsave", s_Molecule_Save, -1);
+ rb_define_method(rb_cMolecule, "set_molecule", s_Molecule_SetMolecule, 1);
+ rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
+ rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
+ rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
+ rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
+
rb_define_method(rb_cMolecule, "name", s_Molecule_Name, 0);
rb_define_method(rb_cMolecule, "set_name", s_Molecule_SetName, 1);
rb_define_method(rb_cMolecule, "path", s_Molecule_Path, 0);
rb_define_method(rb_cMolecule, "dir", s_Molecule_Dir, 0);
rb_define_method(rb_cMolecule, "inspect", s_Molecule_Inspect, 0);
+
rb_define_method(rb_cMolecule, "atoms", s_Molecule_Atoms, 0);
rb_define_method(rb_cMolecule, "bonds", s_Molecule_Bonds, 0);
rb_define_method(rb_cMolecule, "angles", s_Molecule_Angles, 0);
rb_define_method(rb_cMolecule, "nangles", s_Molecule_Nangles, 0);
rb_define_method(rb_cMolecule, "ndihedrals", s_Molecule_Ndihedrals, 0);
rb_define_method(rb_cMolecule, "nimpropers", s_Molecule_Nimpropers, 0);
-
- rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1);
- rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1);
- rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1);
- rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1);
- rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1);
-
- rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
- rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
- rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
- rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
- rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
- rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
-
rb_define_method(rb_cMolecule, "nresidues", s_Molecule_Nresidues, 0);
rb_define_method(rb_cMolecule, "nresidues=", s_Molecule_ChangeNresidues, 1);
rb_define_method(rb_cMolecule, "max_residue_number", s_Molecule_MaxResSeq, -1);
rb_define_method(rb_cMolecule, "min_residue_number", s_Molecule_MinResSeq, -1);
+
rb_define_method(rb_cMolecule, "each_atom", s_Molecule_EachAtom, -1);
- rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
- rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
- rb_define_alias(rb_cMolecule, "set_cell", "cell=");
- rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
- rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
- rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
- rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
- rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
- rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
- rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
- rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
- rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
- rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
- rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
- rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
- rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
- rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
- rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
- rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
+ rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
+ rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
+ rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
+ rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
+
rb_define_method(rb_cMolecule, "extract", s_Molecule_Extract, -1);
rb_define_method(rb_cMolecule, "add", s_Molecule_Add, 1);
rb_define_alias(rb_cMolecule, "+", "add");
rb_define_method(rb_cMolecule, "remove", s_Molecule_Remove, 1);
- rb_define_method(rb_cMolecule, "atom_group", s_Molecule_AtomGroup, -1);
- rb_define_method(rb_cMolecule, "atom_index", s_Molecule_AtomIndex, 1);
rb_define_method(rb_cMolecule, "create_atom", s_Molecule_CreateAnAtom, -1);
rb_define_method(rb_cMolecule, "duplicate_atom", s_Molecule_DuplicateAnAtom, -1);
rb_define_method(rb_cMolecule, "create_bond", s_Molecule_CreateBond, -1);
rb_define_alias(rb_cMolecule, "assign_bond_orders", "assign_bond_order");
rb_define_method(rb_cMolecule, "get_bond_order", s_Molecule_GetBondOrder, 1);
rb_define_alias(rb_cMolecule, "get_bond_orders", "get_bond_order");
+ rb_define_method(rb_cMolecule, "bond_exist?", s_Molecule_BondExist, 2);
rb_define_method(rb_cMolecule, "add_angle", s_Molecule_AddAngle, 3);
rb_define_method(rb_cMolecule, "remove_angle", s_Molecule_RemoveAngle, 3);
rb_define_method(rb_cMolecule, "add_dihedral", s_Molecule_AddDihedral, 4);
rb_define_method(rb_cMolecule, "assign_residue", s_Molecule_AssignResidue, 2);
rb_define_method(rb_cMolecule, "offset_residue", s_Molecule_OffsetResidue, 2);
rb_define_method(rb_cMolecule, "renumber_atoms", s_Molecule_RenumberAtoms, 1);
+
+ rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
+ rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
+ rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
+ rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
+ rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
+
+ rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
+ rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
+ rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
+ rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
+ rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
+ rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
+ rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
rb_define_method(rb_cMolecule, "find_close_atoms", s_Molecule_FindCloseAtoms, -1);
rb_define_method(rb_cMolecule, "guess_bonds", s_Molecule_GuessBonds, -1);
- rb_define_method(rb_cMolecule, "selection", s_Molecule_Selection, 0);
- rb_define_method(rb_cMolecule, "selection=", s_Molecule_SetSelection, 1);
- rb_define_method(rb_cMolecule, "set_undoable_selection", s_Molecule_SetUndoableSelection, 1);
- rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
- rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
- rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
- rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
+
+ rb_define_method(rb_cMolecule, "cell", s_Molecule_Cell, 0);
+ rb_define_method(rb_cMolecule, "cell=", s_Molecule_SetCell, -1);
+ rb_define_alias(rb_cMolecule, "set_cell", "cell=");
+ rb_define_method(rb_cMolecule, "box", s_Molecule_Box, 0);
+ rb_define_method(rb_cMolecule, "box=", s_Molecule_SetBox, 1);
+ rb_define_method(rb_cMolecule, "set_box", s_Molecule_SetBox, -2);
+ rb_define_method(rb_cMolecule, "cell_periodicity", s_Molecule_CellPeriodicity, 0);
+ rb_define_method(rb_cMolecule, "cell_periodicity=", s_Molecule_SetCellPeriodicity, 1);
+ rb_define_alias(rb_cMolecule, "set_cell_periodicity", "cell_periodicity=");
+ rb_define_method(rb_cMolecule, "cell_flexibility", s_Molecule_CellFlexibility, 0);
+ rb_define_method(rb_cMolecule, "cell_flexibility=", s_Molecule_SetCellFlexibility, 1);
+ rb_define_alias(rb_cMolecule, "set_cell_flexibility", "cell_flexibility=");
+ rb_define_method(rb_cMolecule, "cell_transform", s_Molecule_CellTransform, 0);
+ rb_define_method(rb_cMolecule, "symmetry", s_Molecule_Symmetry, 0);
+ rb_define_alias(rb_cMolecule, "symmetries", "symmetry");
+ rb_define_method(rb_cMolecule, "nsymmetries", s_Molecule_Nsymmetries, 0);
+ rb_define_method(rb_cMolecule, "add_symmetry", s_Molecule_AddSymmetry, 1);
+ rb_define_method(rb_cMolecule, "remove_symmetry", s_Molecule_RemoveSymmetry, -1);
+ rb_define_alias(rb_cMolecule, "remove_symmetries", "remove_symmetry");
+ rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
+ rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
+ rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
+
+ rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
+ rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
+ rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
+ rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
+ rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
+ rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
+ rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
+
rb_define_method(rb_cMolecule, "frame=", s_Molecule_SelectFrame, 1);
rb_define_alias(rb_cMolecule, "select_frame", "frame=");
+ rb_define_method(rb_cMolecule, "frame", s_Molecule_Frame, 0);
rb_define_method(rb_cMolecule, "nframes", s_Molecule_Nframes, 0);
- rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
rb_define_method(rb_cMolecule, "insert_frame", s_Molecule_InsertFrames, -1);
+ rb_define_method(rb_cMolecule, "create_frame", s_Molecule_CreateFrames, -1);
rb_define_method(rb_cMolecule, "remove_frame", s_Molecule_RemoveFrames, -1);
rb_define_alias(rb_cMolecule, "create_frames", "create_frame");
rb_define_alias(rb_cMolecule, "insert_frames", "insert_frame");
rb_define_alias(rb_cMolecule, "remove_frames", "remove_frame");
rb_define_method(rb_cMolecule, "each_frame", s_Molecule_EachFrame, 0);
rb_define_method(rb_cMolecule, "get_coord_from_frame", s_Molecule_GetCoordFromFrame, -1);
- rb_define_method(rb_cMolecule, "register_undo", s_Molecule_RegisterUndo, -1);
- rb_define_method(rb_cMolecule, "undo_enabled?", s_Molecule_UndoEnabled, 0);
- rb_define_method(rb_cMolecule, "undo_enabled=", s_Molecule_SetUndoEnabled, 1);
- rb_define_method(rb_cMolecule, "set_atom_attr", s_Molecule_SetAtomAttr, 3);
- rb_define_method(rb_cMolecule, "get_atom_attr", s_Molecule_GetAtomAttr, 2);
+ rb_define_method(rb_cMolecule, "reorder_frames", s_Molecule_ReorderFrames, 1);
+
rb_define_method(rb_cMolecule, "fragment", s_Molecule_Fragment, -1);
rb_define_method(rb_cMolecule, "fragments", s_Molecule_Fragments, -1);
rb_define_method(rb_cMolecule, "each_fragment", s_Molecule_EachFragment, -1);
rb_define_method(rb_cMolecule, "detachable?", s_Molecule_Detachable_P, 1);
rb_define_method(rb_cMolecule, "bonds_on_border", s_Molecule_BondsOnBorder, -1);
- rb_define_method(rb_cMolecule, "translate", s_Molecule_Translate, -1);
- rb_define_method(rb_cMolecule, "rotate", s_Molecule_Rotate, -1);
- rb_define_method(rb_cMolecule, "reflect", s_Molecule_Reflect, -1);
- rb_define_method(rb_cMolecule, "invert", s_Molecule_Invert, -1);
- rb_define_method(rb_cMolecule, "transform", s_Molecule_Transform, -1);
- rb_define_method(rb_cMolecule, "center_of_mass", s_Molecule_CenterOfMass, -1);
- rb_define_method(rb_cMolecule, "centralize", s_Molecule_Centralize, -1);
- rb_define_method(rb_cMolecule, "bounds", s_Molecule_Bounds, -1);
- rb_define_method(rb_cMolecule, "measure_bond", s_Molecule_MeasureBond, 2);
- rb_define_method(rb_cMolecule, "measure_angle", s_Molecule_MeasureAngle, 3);
- rb_define_method(rb_cMolecule, "measure_dihedral", s_Molecule_MeasureDihedral, 4);
- rb_define_method(rb_cMolecule, "expand_by_symmetry", s_Molecule_ExpandBySymmetry, -1);
- rb_define_method(rb_cMolecule, "amend_by_symmetry", s_Molecule_AmendBySymmetry, -1);
- rb_define_method(rb_cMolecule, "transform_for_symop", s_Molecule_TransformForSymop, -1);
- rb_define_method(rb_cMolecule, "symop_for_transform", s_Molecule_SymopForTransform, -1);
- rb_define_method(rb_cMolecule, "wrap_unit_cell", s_Molecule_WrapUnitCell, 1);
- rb_define_method(rb_cMolecule, "find_conflicts", s_Molecule_FindConflicts, -1);
rb_define_method(rb_cMolecule, "fit_coordinates", s_Molecule_FitCoordinates, -1);
+
rb_define_method(rb_cMolecule, "display", s_Molecule_Display, 0);
rb_define_method(rb_cMolecule, "make_front", s_Molecule_MakeFront, 0);
rb_define_method(rb_cMolecule, "update_enabled?", s_Molecule_UpdateEnabled, 0);
rb_define_method(rb_cMolecule, "show_dummy_atoms=", s_Molecule_ShowDummyAtoms, -1);
rb_define_method(rb_cMolecule, "show_expanded", s_Molecule_ShowExpanded, -1);
rb_define_method(rb_cMolecule, "show_expanded=", s_Molecule_ShowExpanded, -1);
- rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
rb_define_method(rb_cMolecule, "show_ellipsoids", s_Molecule_ShowEllipsoids, -1);
rb_define_method(rb_cMolecule, "show_ellipsoids=", s_Molecule_ShowEllipsoids, -1);
+ rb_define_method(rb_cMolecule, "is_atom_visible", s_Molecule_IsAtomVisible, 1);
+ rb_define_method(rb_cMolecule, "hidden_atoms", s_Molecule_HiddenAtoms, 0); /* obsolete */
+ rb_define_method(rb_cMolecule, "hidden_atoms=", s_Molecule_SetHiddenAtoms, 1); /* obsolete */
+ rb_define_alias(rb_cMolecule, "set_hidden_atoms", "hidden_atoms="); /* obsolete */
rb_define_method(rb_cMolecule, "show_graphite", s_Molecule_ShowGraphite, -1);
rb_define_method(rb_cMolecule, "show_graphite=", s_Molecule_ShowGraphite, -1);
rb_define_method(rb_cMolecule, "show_graphite?", s_Molecule_ShowGraphiteFlag, 0);
rb_define_method(rb_cMolecule, "show_periodic_image", s_Molecule_ShowPeriodicImage, -1);
rb_define_method(rb_cMolecule, "show_periodic_image=", s_Molecule_ShowPeriodicImage, -1);
rb_define_method(rb_cMolecule, "show_periodic_image?", s_Molecule_ShowPeriodicImageFlag, 0);
+ rb_define_method(rb_cMolecule, "show_rotation_center", s_Molecule_ShowRotationCenter, -1);
+ rb_define_method(rb_cMolecule, "show_rotation_center=", s_Molecule_ShowRotationCenter, -1);
rb_define_alias(rb_cMolecule, "show_unitcell=", "show_unitcell");
rb_define_alias(rb_cMolecule, "show_unit_cell", "show_unitcell");
rb_define_alias(rb_cMolecule, "show_unit_cell=", "show_unitcell");
rb_define_alias(rb_cMolecule, "show_ellipsoids=", "show_ellipsoids");
rb_define_alias(rb_cMolecule, "show_graphite=", "show_graphite");
rb_define_alias(rb_cMolecule, "show_periodic_image=", "show_periodic_image");
+ rb_define_alias(rb_cMolecule, "show_rotation_center=", "show_rotation_center");
rb_define_method(rb_cMolecule, "line_mode", s_Molecule_LineMode, -1);
rb_define_alias(rb_cMolecule, "line_mode=", "line_mode");
+ rb_define_method(rb_cMolecule, "atom_radius", s_Molecule_AtomRadius, -1);
+ rb_define_alias(rb_cMolecule, "atom_radius=", "atom_radius");
+ rb_define_method(rb_cMolecule, "bond_radius", s_Molecule_BondRadius, -1);
+ rb_define_alias(rb_cMolecule, "bond_radius=", "bond_radius");
+ rb_define_method(rb_cMolecule, "atom_resolution", s_Molecule_AtomResolution, -1);
+ rb_define_alias(rb_cMolecule, "atom_resolution=", "atom_resolution");
+ rb_define_method(rb_cMolecule, "bond_resolution", s_Molecule_BondResolution, -1);
+ rb_define_alias(rb_cMolecule, "bond_resolution=", "bond_resolution");
rb_define_method(rb_cMolecule, "resize_to_fit", s_Molecule_ResizeToFit, 0);
-#if 1 || !defined(__CMDMAC__)
rb_define_method(rb_cMolecule, "get_view_rotation", s_Molecule_GetViewRotation, 0);
rb_define_method(rb_cMolecule, "get_view_scale", s_Molecule_GetViewScale, 0);
rb_define_method(rb_cMolecule, "get_view_center", s_Molecule_GetViewCenter, 0);
rb_define_method(rb_cMolecule, "set_view_scale", s_Molecule_SetViewScale, 1);
rb_define_method(rb_cMolecule, "set_view_center", s_Molecule_SetViewCenter, 1);
rb_define_method(rb_cMolecule, "set_background_color", s_Molecule_SetBackgroundColor, -1);
+ rb_define_method(rb_cMolecule, "export_graphic", s_Molecule_ExportGraphic, -1);
+
+ rb_define_method(rb_cMolecule, "insert_graphic", s_Molecule_InsertGraphic, -1);
rb_define_method(rb_cMolecule, "create_graphic", s_Molecule_CreateGraphic, -1);
rb_define_method(rb_cMolecule, "remove_graphic", s_Molecule_RemoveGraphic, 1);
rb_define_method(rb_cMolecule, "ngraphics", s_Molecule_NGraphics, 0);
- rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, 3);
+ rb_define_method(rb_cMolecule, "get_graphic_point", s_Molecule_GetGraphicPoint, -1);
+ rb_define_method(rb_cMolecule, "set_graphic_point", s_Molecule_SetGraphicPoint, -1);
+ rb_define_alias(rb_cMolecule, "get_graphic_points", "get_graphic_point");
+ rb_define_alias(rb_cMolecule, "set_graphic_points", "set_graphic_point");
+ rb_define_method(rb_cMolecule, "get_graphic_color", s_Molecule_SetGraphicColor, 1);
rb_define_method(rb_cMolecule, "set_graphic_color", s_Molecule_SetGraphicColor, 2);
rb_define_method(rb_cMolecule, "show_graphic", s_Molecule_ShowGraphic, 1);
rb_define_method(rb_cMolecule, "hide_graphic", s_Molecule_HideGraphic, 1);
-#endif
rb_define_method(rb_cMolecule, "show_text", s_Molecule_ShowText, 1);
+
rb_define_method(rb_cMolecule, "md_arena", s_Molecule_MDArena, 0);
rb_define_method(rb_cMolecule, "set_parameter_attr", s_Molecule_SetParameterAttr, 5);
rb_define_method(rb_cMolecule, "parameter", s_Molecule_Parameter, 0);
+ rb_define_method(rb_cMolecule, "start_step", s_Molecule_StartStep, 0);
+ rb_define_method(rb_cMolecule, "start_step=", s_Molecule_SetStartStep, 1);
+ rb_define_method(rb_cMolecule, "steps_per_frame", s_Molecule_StepsPerFrame, 0);
+ rb_define_method(rb_cMolecule, "steps_per_frame=", s_Molecule_SetStepsPerFrame, 1);
+ rb_define_method(rb_cMolecule, "ps_per_step", s_Molecule_PsPerStep, 0);
+ rb_define_method(rb_cMolecule, "ps_per_step=", s_Molecule_SetPsPerStep, 1);
+ rb_define_method(rb_cMolecule, "bond_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
+ rb_define_method(rb_cMolecule, "angle_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
+ rb_define_method(rb_cMolecule, "dihedral_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
+ rb_define_method(rb_cMolecule, "improper_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
+ rb_define_method(rb_cMolecule, "vdw_par", s_Molecule_BondParIsObsolete, 1); /* Obsolete */
+
rb_define_method(rb_cMolecule, "selected_MO", s_Molecule_SelectedMO, 0);
rb_define_method(rb_cMolecule, "default_MO_grid", s_Molecule_GetDefaultMOGrid, -1);
rb_define_method(rb_cMolecule, "cubegen", s_Molecule_Cubegen, -1);
+ rb_define_method(rb_cMolecule, "clear_surface", s_Molecule_ClearSurface, 0);
+ rb_define_method(rb_cMolecule, "show_surface", s_Molecule_ShowSurface, 0);
+ rb_define_method(rb_cMolecule, "hide_surface", s_Molecule_HideSurface, 0);
+ rb_define_method(rb_cMolecule, "create_surface", s_Molecule_CreateSurface, -1);
+ rb_define_method(rb_cMolecule, "set_surface_attr", s_Molecule_SetSurfaceAttr, 1);
rb_define_method(rb_cMolecule, "nelpots", s_Molecule_NElpots, 0);
rb_define_method(rb_cMolecule, "elpot", s_Molecule_Elpot, 1);
+ rb_define_method(rb_cMolecule, "clear_basis_set", s_Molecule_ClearBasisSet, 0);
rb_define_method(rb_cMolecule, "add_gaussian_orbital_shell", s_Molecule_AddGaussianOrbitalShell, 3);
rb_define_method(rb_cMolecule, "add_gaussian_primitive_coefficients", s_Molecule_AddGaussianPrimitiveCoefficients, 3);
- rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
+ rb_define_method(rb_cMolecule, "get_gaussian_shell_info", s_Molecule_GetGaussianShellInfo, 1);
+ rb_define_method(rb_cMolecule, "get_gaussian_primitive_coefficients", s_Molecule_GetGaussianPrimitiveCoefficients, 1);
+ rb_define_method(rb_cMolecule, "get_gaussian_component_info", s_Molecule_GetGaussianComponentInfo, 1);
+ rb_define_method(rb_cMolecule, "clear_mo_coefficients", s_Molecule_ClearMOCoefficients, 0);
rb_define_method(rb_cMolecule, "set_mo_coefficients", s_Molecule_SetMOCoefficients, 3);
- rb_define_method(rb_cMolecule, "allocate_basis_set_record", s_Molecule_AllocateBasisSetRecord, 3);
+ rb_define_method(rb_cMolecule, "get_mo_coefficients", s_Molecule_GetMOCoefficients, 1);
+ rb_define_method(rb_cMolecule, "get_mo_energy", s_Molecule_GetMOEnergy, 1);
+ rb_define_method(rb_cMolecule, "set_mo_info", s_Molecule_SetMOInfo, 1);
+ rb_define_method(rb_cMolecule, "get_mo_info", s_Molecule_GetMOInfo, 1);
+ rb_define_method(rb_cMolecule, "mo_type", s_Molecule_MOType, 0);
+
rb_define_method(rb_cMolecule, "search_equivalent_atoms", s_Molecule_SearchEquivalentAtoms, -1);
-
rb_define_method(rb_cMolecule, "create_pi_anchor", s_Molecule_CreatePiAnchor, -1);
- rb_define_method(rb_cMolecule, "==", s_Molecule_Equal, 1);
- rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
-
+
+ rb_define_method(rb_cMolecule, "set_property", s_Molecule_SetProperty, -1);
+ rb_define_method(rb_cMolecule, "get_property", s_Molecule_GetProperty, -1);
+ rb_define_method(rb_cMolecule, "property_names", s_Molecule_PropertyNames, 0);
+
rb_define_singleton_method(rb_cMolecule, "current", s_Molecule_Current, 0);
rb_define_singleton_method(rb_cMolecule, "[]", s_Molecule_MoleculeAtIndex, -1);
- rb_define_singleton_method(rb_cMolecule, "open", s_Molecule_Open, -1);
rb_define_singleton_method(rb_cMolecule, "list", s_Molecule_List, 0);
rb_define_singleton_method(rb_cMolecule, "ordered_list", s_Molecule_OrderedList, 0);
- rb_define_singleton_method(rb_cMolecule, "error_message", s_Molecule_ErrorMessage, 0);
- rb_define_singleton_method(rb_cMolecule, "set_error_message", s_Molecule_SetErrorMessage, 1);
- rb_define_singleton_method(rb_cMolecule, "error_message=", s_Molecule_SetErrorMessage, 1);
+
+ rb_define_method(rb_cMolecule, "call_subprocess_async", s_Molecule_CallSubProcessAsync, -1);
/* class MolEnumerable */
rb_cMolEnumerable = rb_define_class_under(rb_mMolby, "MolEnumerable", rb_cObject);
rb_define_alias(rb_cAtomRef, "set_attr", "[]=");
rb_define_method(rb_cAtomRef, "[]", s_AtomRef_GetAttr, 1);
rb_define_alias(rb_cAtomRef, "get_attr", "[]");
- s_SetAtomAttrString = rb_str_new2("set_atom_attr");
+ s_SetAtomAttrString = Ruby_NewEncodedStringValue2("set_atom_attr");
rb_global_variable(&s_SetAtomAttrString);
rb_define_method(rb_cAtomRef, "molecule", s_AtomRef_GetMolecule, 0);
rb_define_method(rb_cAtomRef, "==", s_AtomRef_Equal, 1);
rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
rb_define_method(rb_mKernel, "ask", s_Kernel_Ask, -1);
- rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, 2);
+ rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
rb_define_method(rb_mKernel, "lookup_menu", s_Kernel_LookupMenu, 1);
rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
rb_define_method(rb_mKernel, "bell", s_Kernel_Bell, 0);
rb_define_method(rb_mKernel, "play_sound", s_Kernel_PlaySound, -1);
rb_define_method(rb_mKernel, "stop_sound", s_Kernel_StopSound, 0);
-
+ rb_define_method(rb_mKernel, "export_to_clipboard", s_Kernel_ExportToClipboard, 1);
+ rb_define_method(rb_mKernel, "hartree_to_kcal", s_Kernel_HartreeToKcal, 1);
+ rb_define_method(rb_mKernel, "hartree_to_kj", s_Kernel_HartreeToKJ, 1);
+ rb_define_method(rb_mKernel, "kcal_to_hartree", s_Kernel_KcalToHartree, 1);
+ rb_define_method(rb_mKernel, "kj_to_hartree", s_Kernel_KJToHartree, 1);
+ rb_define_method(rb_mKernel, "bohr_to_angstrom", s_Kernel_BohrToAngstrom, 1);
+ rb_define_method(rb_mKernel, "angstrom_to_bohr", s_Kernel_AngstromToBohr, 1);
+
+ /* class IO */
+ rb_define_method(rb_cIO, "gets_any_eol", s_IO_gets_any_eol, 0);
+
s_ID_equal = rb_intern("==");
g_RubyID_call = rb_intern("call");
+
+ s_InitMOInfoKeys();
+
+ /* Symbols for graphics */
+ s_LineSym = ID2SYM(rb_intern("line"));
+ s_PolySym = ID2SYM(rb_intern("poly"));
+ s_CylinderSym = ID2SYM(rb_intern("cylinder"));
+ s_ConeSym = ID2SYM(rb_intern("cone"));
+ s_EllipsoidSym = ID2SYM(rb_intern("ellipsoid"));
+}
+
+#pragma mark ====== Interface with RubyDialog class ======
+
+RubyValue
+RubyDialogCallback_parentModule(void)
+{
+ return (RubyValue)rb_mMolby;
}
#pragma mark ====== External functions ======
static VALUE s_ruby_top_self = Qfalse;
+static VALUE s_ruby_get_binding_for_molecule = Qfalse;
+static VALUE s_ruby_export_local_variables = Qfalse;
static VALUE
s_evalRubyScriptOnMoleculeSub(VALUE val)
{
void **ptr = (void **)val;
Molecule *mol = (Molecule *)ptr[1];
- VALUE sval = rb_str_new2((char *)ptr[0]);
- VALUE fnval;
+ VALUE sval, fnval, lnval, retval;
+ VALUE binding;
+
+ /* Clear the error information (store in the history array if necessary) */
+ sval = rb_errinfo();
+ if (sval != Qnil) {
+ rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
+ rb_set_errinfo(Qnil);
+ }
+
if (s_ruby_top_self == Qfalse) {
s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
}
+ if (s_ruby_get_binding_for_molecule == Qfalse) {
+ const char *s1 =
+ "lambda { |_mol_, _bind_| \n"
+ " _proc_ = eval(\"lambda { |__mol__| __mol__.instance_eval { binding } } \", _bind_) \n"
+ " _proc_.call(_mol_) } ";
+ s_ruby_get_binding_for_molecule = rb_eval_string(s1);
+ rb_define_variable("_get_binding_for_molecule", &s_ruby_get_binding_for_molecule);
+ }
+ if (s_ruby_export_local_variables == Qfalse) {
+ const char *s2 =
+ "lambda { |_bind_| \n"
+ " # find local variables newly defined in _bind_ \n"
+ " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
+ " _a_.each { |_vsym_| \n"
+ " _vname_ = _vsym_.to_s \n"
+ " _vval_ = _bind_.eval(_vname_) \n"
+ " # Define local variable \n"
+ " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
+ " # Then set value \n"
+ " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
+ " } \n"
+ "}";
+ s_ruby_export_local_variables = rb_eval_string(s2);
+ rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
+ }
if (ptr[2] == NULL) {
- fnval = Qnil;
+ char *scr;
+ /* String literal: we need to specify string encoding */
+#if defined(__WXMSW__)
+ asprintf(&scr, "#coding:shift_jis\n%s", (char *)ptr[0]);
+#else
+ asprintf(&scr, "#coding:utf-8\n%s", (char *)ptr[0]);
+#endif
+ sval = Ruby_NewEncodedStringValue2(scr);
+ free(scr);
+ fnval = Ruby_NewEncodedStringValue2("(eval)");
+ lnval = INT2FIX(0);
} else {
+ sval = Ruby_NewEncodedStringValue2((char *)ptr[0]);
fnval = Ruby_NewFileStringValue((char *)ptr[2]);
+ lnval = INT2FIX(1);
}
- if (mol == NULL) {
- if (fnval == Qnil)
- return rb_funcall(s_ruby_top_self, rb_intern("eval"), 1, sval);
- else
- return rb_funcall(s_ruby_top_self, rb_intern("eval"), 4, sval, Qnil, fnval, INT2FIX(1));
- } else {
+ binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
+ if (mol != NULL) {
VALUE mval = ValueFromMolecule(mol);
- if (fnval == Qnil)
- return rb_funcall(mval, rb_intern("instance_eval"), 1, sval);
- else return rb_funcall(mval, rb_intern("instance_eval"), 3, sval, fnval, INT2FIX(1));
+ binding = rb_funcall(s_ruby_get_binding_for_molecule, rb_intern("call"), 2, mval, binding);
}
+ retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
+ if (mol != NULL) {
+ rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
+ }
+ return retval;
}
RubyValue
RubyValue retval;
void *args[3];
VALUE save_interrupt_flag;
- char *save_ruby_sourcefile;
- int save_ruby_sourceline;
+/* char *save_ruby_sourcefile;
+ int save_ruby_sourceline; */
if (gMolbyIsCheckingInterrupt) {
MolActionAlertRubyIsRunning();
*status = -1;
args[1] = (void *)mol;
args[2] = (void *)fname;
save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
- save_ruby_sourcefile = ruby_sourcefile;
- save_ruby_sourceline = ruby_sourceline;
+/* save_ruby_sourcefile = ruby_sourcefile;
+ save_ruby_sourceline = ruby_sourceline; */
retval = (RubyValue)rb_protect(s_evalRubyScriptOnMoleculeSub, (VALUE)args, status);
if (*status != 0) {
/* Is this 'exit' exception? */
/* Capture exit and return the status value */
retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
*status = 0;
+ rb_set_errinfo(Qnil);
}
}
s_SetInterruptFlag(Qnil, save_interrupt_flag);
- ruby_sourcefile = save_ruby_sourcefile;
- ruby_sourceline = save_ruby_sourceline;
+/* ruby_sourcefile = save_ruby_sourcefile;
+ ruby_sourceline = save_ruby_sourceline; */
gMolbyRunLevel--;
return retval;
}
-void
-Molby_showRubyValue(RubyValue value, char **outValueString)
+/* For debug */
+char *
+Ruby_inspectValue(RubyValue value)
+{
+ int status;
+ static char buf[256];
+ VALUE val = (VALUE)value;
+ gMolbyRunLevel++;
+ val = rb_protect(rb_inspect, val, &status);
+ gMolbyRunLevel--;
+ if (status == 0) {
+ char *str = StringValuePtr(val);
+ strncpy(buf, str, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+ } else {
+ snprintf(buf, sizeof(buf), "Error status = %d", status);
+ }
+ return buf;
+}
+
+int
+Ruby_showValue(RubyValue value, char **outValueString)
{
VALUE val = (VALUE)value;
if (gMolbyIsCheckingInterrupt) {
MolActionAlertRubyIsRunning();
- return;
+ return 0;
}
if (val != Qnil) {
int status;
gMolbyRunLevel++;
val = rb_protect(rb_inspect, val, &status);
gMolbyRunLevel--;
+ if (status != 0)
+ return status;
str = StringValuePtr(val);
if (outValueString != NULL)
*outValueString = strdup(str);
MyAppCallback_showScriptMessage("%s", str);
+ } else {
+ if (outValueString != NULL)
+ *outValueString = NULL;
}
+ return 0;
}
void
-Molby_showError(int status)
+Ruby_showError(int status)
{
static const int tag_raise = 6;
+ char *main_message = "Molby script error";
char *msg = NULL, *msg2;
VALUE val, backtrace;
int interrupted = 0;
+ int exit_status = -1;
if (status == tag_raise) {
- VALUE eclass = CLASS_OF(ruby_errinfo);
+ VALUE errinfo = rb_errinfo();
+ VALUE eclass = CLASS_OF(errinfo);
if (eclass == rb_eInterrupt) {
- msg = "Interrupt";
+ main_message = "Molby script interrupted";
+ msg = "Interrupt";
interrupted = 1;
- }
+ } else if (eclass == rb_eSystemExit) {
+ main_message = "Molby script exit";
+ interrupted = 2;
+ val = rb_eval_string_protect("$!.status", &status);
+ if (status == 0) {
+ exit_status = NUM2INT(rb_Integer(val));
+ asprintf(&msg, "Molby script exit with status %d", exit_status);
+ } else {
+ asprintf(&msg, "Molby script exit with unknown status");
+ }
+ }
}
gMolbyRunLevel++;
- backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
- if (msg == NULL) {
- val = rb_eval_string_protect("$!.to_s", &status);
- if (status == 0)
- msg = RSTRING_PTR(val);
- else msg = "(message not available)";
- }
- asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
- MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Molby script error" : "Molby script interrupted"), 0, 3);
+ if (exit_status != 0) {
+ backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
+ if (msg == NULL) {
+ val = rb_eval_string_protect("$!.to_s", &status);
+ if (status == 0)
+ msg = RSTRING_PTR(val);
+ else
+ msg = "(message not available)";
+ }
+ asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
+ } else {
+ msg2 = strdup(msg);
+ }
+ MyAppCallback_messageBox(msg2, main_message, 0, 3);
free(msg2);
+ if (interrupted == 2) {
+ free(msg);
+ if (!gUseGUI && exit_status == 0)
+ exit(0); // Capture exit(0) here and force exit
+ }
gMolbyRunLevel--;
}
-char *
-Molby_getDescription(void)
+/* Wrapper function for rb_load_protect or rb_eval_string_protect. Used only in non-GUI mode. */
+int
+Molby_loadScript(const char *script, int from_file)
+{
+ int status;
+ gMolbyRunLevel++;
+ if (from_file)
+ rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
+ else
+ rb_eval_string_protect(script, &status);
+ gMolbyRunLevel--;
+ return status;
+}
+
+void
+Molby_getDescription(char **versionString, char **auxString)
{
extern const char *gVersionString, *gCopyrightString;
extern int gRevisionNumber;
extern char *gLastBuildString;
- char *s;
+ char *s1, *s2;
char *revisionString;
if (gRevisionNumber > 0) {
asprintf(&revisionString, ", revision %d", gRevisionNumber);
} else revisionString = "";
- asprintf(&s,
- "Molby %s%s\n%s\nLast compile: %s\n"
-#if !defined(__CMDMAC__)
- "\nIncluding:\n"
- "%s"
+
+ asprintf(&s1, "%s %s%s\n%s\nLast compile: %s\n",
+#if defined(__WXMSW__)
+ #if TARGET_ARCH == 64
+ "Molby (64 bit)",
+ #else
+ "Molby (32 bit)",
+ #endif
#else
- "Including "
+ "Molby",
#endif
- "ruby %s, http://www.ruby-lang.org/\n"
- "%s\n"
- "FFTW 3.3.2, http://www.fftw.org/\n"
- " Copyright (C) 2003, 2007-11 Matteo Frigo\n"
- " and Massachusetts Institute of Technology",
- gVersionString, revisionString, gCopyrightString, gLastBuildString,
-#if !defined(__CMDMAC__)
- MyAppCallback_getGUIDescriptionString(),
-#endif
- gRubyVersion, gRubyCopyright);
+ gVersionString, revisionString, gCopyrightString, gLastBuildString);
+ if (gUseGUI) {
+ asprintf(&s2,
+ "\nIncluding:\n"
+ "%s"
+ "ruby %s, http://www.ruby-lang.org/\n"
+ "%s\n"
+ "FFTW 3.3.2, http://www.fftw.org/\n"
+ " Copyright (C) 2003, 2007-11 Matteo Frigo"
+ " and Massachusetts Institute of Technology",
+ MyAppCallback_getGUIDescriptionString(),
+ gRubyVersion, gRubyCopyright);
+ } else {
+ asprintf(&s2,
+ "Including "
+ "ruby %s, http://www.ruby-lang.org/\n"
+ "%s\n"
+ "FFTW 3.3.2, http://www.fftw.org/\n"
+ " Copyright (C) 2003, 2007-11 Matteo Frigo"
+ " and Massachusetts Institute of Technology",
+ gRubyVersion, gRubyCopyright);
+
+ }
if (revisionString[0] != 0)
free(revisionString);
- return s;
+ if (versionString != NULL)
+ *versionString = s1;
+ if (auxString != NULL)
+ *auxString = s2;
}
void
{
gRubyVersion = strdup(ruby_version);
asprintf(&gRubyCopyright, "%sCopyright (C) %d-%d %s",
-#if defined(__CMDMAC__)
- "",
-#else
" ", /* Indent for displaying in About dialog */
-#endif
RUBY_BIRTH_YEAR, RUBY_RELEASE_YEAR, RUBY_AUTHOR);
}
}
} */
-#if defined(__CMDMAC__)
- wbuf = Molby_getDescription();
- printf("%s\n", wbuf);
- free(wbuf);
-#endif
+ if (!gUseGUI) {
+ char *wbuf2;
+ Molby_getDescription(&wbuf, &wbuf2);
+ MyAppCallback_showScriptMessage("%s\n%s\n", wbuf, wbuf2);
+ free(wbuf);
+ free(wbuf2);
+ }
/* Read atom display parameters */
if (ElementParameterInitialize("element.par", &wbuf) != 0) {
-#if defined(__CMDMAC__)
- fprintf(stderr, "%s\n", wbuf);
-#else
- MyAppCallback_setConsoleColor(1);
- MyAppCallback_showScriptMessage("%s", wbuf);
- MyAppCallback_setConsoleColor(0);
-#endif
+ MyAppCallback_setConsoleColor(1);
+ MyAppCallback_showScriptMessage("%s", wbuf);
+ MyAppCallback_setConsoleColor(0);
free(wbuf);
}
/* Read default parameters */
ParameterReadFromFile(gBuiltinParameters, "default.par", &wbuf, NULL);
if (wbuf != NULL) {
-#if defined(__CMDMAC__)
- fprintf(stderr, "%s\n", wbuf);
-#else
- MyAppCallback_setConsoleColor(1);
- MyAppCallback_showScriptMessage("%s", wbuf);
- MyAppCallback_setConsoleColor(0);
-#endif
+ MyAppCallback_setConsoleColor(1);
+ MyAppCallback_showScriptMessage("%s", wbuf);
+ MyAppCallback_setConsoleColor(0);
free(wbuf);
}
/* Initialize Ruby interpreter */
+#if __WXMSW__
+ if (gUseGUI) {
+ /* On Windows, fileno(stdin|stdout|stderr) returns -2 and
+ it causes rb_bug() (= fatal error) during ruby_init().
+ As a workaround, these standard streams are reopend as
+ NUL stream. */
+ freopen("NUL", "r", stdin);
+ freopen("NUL", "w", stdout);
+ freopen("NUL", "w", stderr);
+ }
+#endif
ruby_init();
-
+
+ {
+ /* Initialize CP932/Windows-31J encodings */
+ extern void Init_shift_jis(void), Init_windows_31j(void), Init_trans_japanese_sjis(void);
+ extern int rb_enc_alias(const char *, const char *);
+ Init_shift_jis();
+ Init_windows_31j();
+ Init_trans_japanese_sjis();
+ rb_enc_alias("CP932", "Windows-31J");
+ }
+
+#if defined(__WXMSW__)
+ {
+ /* Set default external encoding */
+ /* The following snippet is taken from encoding.c */
+ extern void rb_enc_set_default_external(VALUE encoding);
+ char cp[sizeof(int) * 8 / 3 + 22];
+ int status;
+ VALUE enc;
+ snprintf(cp, sizeof cp, "Encoding.find('CP%d')", AreFileApisANSI() ? GetACP() : GetOEMCP());
+ enc = rb_eval_string_protect(cp, &status);
+ if (status == 0 && !NIL_P(enc)) {
+ rb_enc_set_default_external(enc);
+ }
+ }
+#endif
+
/* Initialize loadpath; the specified directory, "lib" subdirectory, and "." */
ruby_incpush(".");
asprintf(&libpath, "%s%clib", dir, PATH_SEPARATOR);
/* Define Molby classes */
Init_Molby();
- RubyDialogInitClass();
+ if (gUseGUI)
+ RubyDialogInitClass();
rb_define_const(rb_mMolby, "ResourcePath", val);
val = Ruby_NewFileStringValue(dir);
rb_define_const(rb_mMolby, "MbsfPath", val);
free(p);
-#if defined(__CMDMAC__)
- rb_define_const(rb_mMolby, "HasGUI", Qfalse);
-#else
- rb_define_const(rb_mMolby, "HasGUI", Qtrue);
-#endif
-
-#if !__CMDMAC__
-
- /* Create objects for stdout and stderr */
- val = rb_funcall(rb_cObject, rb_intern("new"), 0);
- rb_define_singleton_method(val, "write", s_StandardOutput, 1);
- rb_gv_set("$stdout", val);
- val = rb_funcall(rb_cObject, rb_intern("new"), 0);
- rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
- rb_gv_set("$stderr", val);
-
- /* Create objects for stdin */
- val = rb_funcall(rb_cObject, rb_intern("new"), 0);
- rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
- rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
- rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
- rb_gv_set("$stdin", val);
+ p = MyAppCallback_getHomeDir();
+ val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
+ rb_define_const(rb_mMolby, "HomeDirectory", val);
+ free(p);
+ p = MyAppCallback_getDocumentHomeDir();
+ val = (p == NULL ? Qnil : Ruby_NewFileStringValue(p));
+ rb_define_const(rb_mMolby, "DocumentDirectory", val);
+ free(p);
-#endif
+ if (gUseGUI)
+ rb_define_const(rb_mMolby, "HasGUI", Qtrue);
+ else
+ rb_define_const(rb_mMolby, "HasGUI", Qfalse);
+
+ {
+ /* Create objects for stdout and stderr */
+ val = rb_funcall(rb_cObject, rb_intern("new"), 0);
+ rb_define_singleton_method(val, "write", s_StandardOutput, 1);
+ rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
+ rb_gv_set("$stdout", val);
+ val = rb_funcall(rb_cObject, rb_intern("new"), 0);
+ rb_define_singleton_method(val, "write", s_StandardErrorOutput, 1);
+ rb_define_singleton_method(val, "flush", s_FlushConsoleOutput, 0);
+ rb_gv_set("$stderr", val);
+
+ /* Create objects for stdin */
+ val = rb_funcall(rb_cObject, rb_intern("new"), 0);
+ rb_define_singleton_method(val, "gets", s_StandardInputGets, -1);
+ rb_define_singleton_method(val, "readline", s_StandardInputGets, -1);
+ rb_define_singleton_method(val, "method_missing", s_StandardInputMethodMissing, -1);
+ rb_gv_set("$stdin", val);
+ }
- /* Global variable to hold backtrace */
+ /* Global variable to hold error information */
rb_define_variable("$backtrace", &gMolbyBacktrace);
-
-#if !__CMDMAC__
- /* Register interrupt check code */
- rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL);
-#endif
+ rb_define_variable("$error_history", &gMolbyErrorHistory);
+ gMolbyErrorHistory = rb_ary_new();
-#if !__CMDMAC__
- /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
- s_SetIntervalTimer(0, 50);
-#endif
+ /* Global variables for script menus */
+ rb_define_variable("$script_menu_commands", &gScriptMenuCommands);
+ rb_define_variable("$script_menu_enablers", &gScriptMenuEnablers);
+ gScriptMenuCommands = rb_ary_new();
+ gScriptMenuEnablers = rb_ary_new();
+
+ if (gUseGUI) {
+ /* Register interrupt check code */
+ rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
+ /* Start interval timer (for periodic polling of interrupt); firing every 50 msec */
+ s_SetIntervalTimer(0, 50);
+ }
/* Read the startup script */
if (script != NULL && script[0] != 0) {
MyAppCallback_showScriptMessage("Evaluating %s...\n", script);
gMolbyRunLevel++;
- rb_load_protect(rb_str_new2(script), 0, &status);
+ rb_load_protect(Ruby_NewEncodedStringValue2(script), 0, &status);
gMolbyRunLevel--;
if (status != 0)
- Molby_showError(status);
+ Ruby_showError(status);
else
MyAppCallback_showScriptMessage("Done.\n");
}