OSDN Git Service

Implement loading/saving of graphiobjects from/to mbsf files.
authortoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Fri, 12 Sep 2014 11:11:21 +0000 (11:11 +0000)
committertoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Fri, 12 Sep 2014 11:11:21 +0000 (11:11 +0000)
git-svn-id: svn+ssh://svn.sourceforge.jp/svnroot/molby/trunk@570 a2be9bc6-48de-4e38-9406-05402d4bc13c

MolLib/Molecule.c
MolLib/Ruby_bind/ruby_bind.c
Scripts/loadsave.rb

index 441eaa1..043b4c1 100755 (executable)
@@ -1602,15 +1602,23 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                                        continue;
                                if (buf[0] == '\n')
                                        break;
+                               if (mp->mview == NULL || mp->mview->track == NULL)
+                                       continue;  /*  Skip (this should not happen though)  */
                                /* scale; trx try trz; theta_deg x y z */
-                               if ((i == 0 && sscanf(buf, "%lf", &mview_dbuf[0]) < 1)
+                               if ((i == 0 && sscanf(buf, "%lf", &dbuf[0]) < 1)
                                        || (i == 1 && sscanf(buf, "%lf %lf %lf",
-                                                                                &mview_dbuf[1], &mview_dbuf[2], &mview_dbuf[3]) < 3)
+                                                                                &dbuf[1], &dbuf[2], &dbuf[3]) < 3)
                                        || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
-                                                                                &mview_dbuf[4], &mview_dbuf[5], &mview_dbuf[6], &mview_dbuf[7]) < 4)) {
+                                                                                &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 4)) {
                                        s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
                                        goto err_exit;
                                }
+                               if (i == 0)
+                                       TrackballSetScale(mp->mview->track, dbuf[0]);
+                               else if (i == 1)
+                                       TrackballSetTranslate(mp->mview->track, dbuf + 1);
+                               else if (i == 2)
+                                       TrackballSetRotate(mp->mview->track, dbuf + 4);
                                i++;
                        }
                        continue;
@@ -1620,6 +1628,8 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                                        continue;
                                if (buf[0] == '\n')
                                        break;
+                               if (mp->mview == NULL)
+                                       continue;  /*  Skip (this should not happen, though)  */
                                bufp = buf;
                                comp = strsep(&bufp, " \t");
                                if (bufp != NULL) {
@@ -1627,27 +1637,40 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                                                bufp++;
                                        valp = strsep(&bufp, "\n");
                                } else valp = NULL;
-                               /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
-                               if ((strcmp(comp, "show_unit_cell") == 0 && (i = 1))
-                                       || (strcmp(comp, "show_periodic_box") == 0 && (i = 2))
-                                       || (strcmp(comp, "show_expanded_atoms") == 0 && (i = 3))
-                                       || (strcmp(comp, "show_ellipsoids") == 0 && (i = 4))
-                                       || (strcmp(comp, "show_hydrogens") == 0 && (i = 5))
-                                       || (strcmp(comp, "show_dummy_atoms") == 0 && (i = 6))
-                                       || (strcmp(comp, "show_rotation_center") == 0 && (i = 7))
-                                       || (strcmp(comp, "show_graphite_flag") == 0 && (i = 8))
-                                       || (strcmp(comp, "show_periodic_image_flag") == 0 && (i = 9))
-                                       || (strcmp(comp, "show_graphite") == 0 && (i = 10))
-                                       || (strcmp(comp, "atom_resolution") == 0 && (i = 11))
-                                       || (strcmp(comp, "bond_resolution") == 0 && (i = 12))) {
-                                       mview_ibuf[i - 1] = atoi(valp);
-                               } else if ((strcmp(comp, "atom_radius") == 0 && (i = 8))
-                                       || (strcmp(comp, "bond_radius") == 0 && (i = 9))) {
-                                       mview_dbuf[i] = strtod(valp, NULL);
-                               } else if (strcmp(comp, "show_periodic_image") == 0) {
-                                       sscanf(valp, "%d %d %d %d %d %d",
-                                                  &mview_ibuf[12], &mview_ibuf[13], &mview_ibuf[14],
-                                                  &mview_ibuf[15], &mview_ibuf[16], &mview_ibuf[17]);
+                               if (strcmp(comp, "show_unit_cell") == 0)
+                                       mp->mview->showUnitCell = atoi(valp);
+                               else if (strcmp(comp, "show_periodic_box") == 0)
+                                       mp->mview->showPeriodicBox = atoi(valp);
+                               else if (strcmp(comp, "show_expanded_atoms") == 0)
+                                       mp->mview->showExpandedAtoms = atoi(valp);
+                               else if (strcmp(comp, "show_ellipsoids") == 0)
+                                       mp->mview->showEllipsoids = atoi(valp);
+                               else if (strcmp(comp, "show_hydrogens") == 0)
+                                       mp->mview->showHydrogens = atoi(valp);
+                               else if (strcmp(comp, "show_dummy_atoms") == 0)
+                                       mp->mview->showDummyAtoms = atoi(valp);
+                               else if (strcmp(comp, "show_rotation_center") == 0)
+                                       mp->mview->showRotationCenter = atoi(valp);
+                               else if (strcmp(comp, "show_graphite_flag") == 0)
+                                       mp->mview->showGraphiteFlag = atoi(valp);
+                               else if (strcmp(comp, "show_periodic_image_flag") == 0)
+                                       mp->mview->showPeriodicImageFlag = atoi(valp);
+                               else if (strcmp(comp, "show_graphite") == 0)
+                                       mp->mview->showGraphite = atoi(valp);
+                               else if (strcmp(comp, "show_expanded_atoms") == 0)
+                                       mp->mview->showExpandedAtoms = atoi(valp);
+                               else if (strcmp(comp, "atom_resolution") == 0 && (i = atoi(valp)) >= 6)
+                                       mp->mview->atomResolution = i;
+                               else if (strcmp(comp, "bond_resolution") == 0 && (i = atoi(valp)) >= 4)
+                                       mp->mview->bondResolution = i;
+                               else if (strcmp(comp, "atom_radius") == 0)
+                                       mp->mview->atomRadius = strtod(valp, NULL);
+                               else if (strcmp(comp, "bond_radius") == 0)
+                                       mp->mview->bondRadius = strtod(valp, NULL);
+                               else if (strcmp(comp, "show_periodic_image") == 0) {
+                                       sscanf(valp, "%d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5]);
+                                       for (i = 0; i < 6; i++)
+                                               mp->mview->showPeriodicImage[i] = ibuf[i];
                                }
                        }
                        continue;
@@ -1826,6 +1849,121 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                                i = ibuf[0] + 1;  /*  For next entry  */
                        }
                        continue;
+               } else if (strcmp(buf, "!:graphics") == 0) {
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               MainViewGraphic *gp = NULL;
+                               if (buf[0] == '!')
+                                       continue;
+                               if (buf[0] == '\n')
+                                       break;
+                               if (mp->mview == NULL)
+                                       continue;  /*  Skip  */
+                               if (strcmp(buf, "line\n") == 0) {
+                                       ibuf[0] = kMainViewGraphicLine;
+                               } else if (strcmp(buf, "poly\n") == 0) {
+                                       ibuf[0] = kMainViewGraphicPoly;
+                               } else if (strcmp(buf, "cylinder\n") == 0) {
+                                       ibuf[0] = kMainViewGraphicCylinder;
+                               } else if (strcmp(buf, "cone\n") == 0) {
+                                       ibuf[0] = kMainViewGraphicCone;
+                               } else if (strcmp(buf, "ellipsoid\n") == 0) {
+                                       ibuf[0] = kMainViewGraphicEllipsoid;
+                               } else {
+                                       continue;  /*  Skip  */
+                               }
+                               gp = (MainViewGraphic *)calloc(sizeof(MainViewGraphic), 1);
+                               gp->kind = ibuf[0];
+                               i = 0;
+                               while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                                       if (buf[0] == '!')
+                                               continue;
+                                       if (buf[0] == '\n')
+                                               break;
+                                       if (i == 0) {
+                                               if (sscanf(buf, "%d %d", &ibuf[0], &ibuf[1]) < 2) {
+                                                       s_append_asprintf(errbuf, "line %d: the closed/visible flags cannot be read for graphic object", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               gp->closed = ibuf[0];
+                                               gp->visible = ibuf[1];
+                                       } else if (i == 1) {
+                                               if (sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 4) {
+                                                       s_append_asprintf(errbuf, "line %d: the color cannot be read for graphic object", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               for (j = 0; j < 4; j++)
+                                                       gp->rgba[j] = dbuf[j];
+                                       } else if (i == 2) {
+                                               j = atoi(buf);
+                                               if (j < 0) {
+                                                       s_append_asprintf(errbuf, "line %d: the number of control points must be non-negative", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               if (j > 0)
+                                                       NewArray(&gp->points, &gp->npoints, sizeof(GLfloat) * 3, j);
+                                       } else if (i >= 3 && i < gp->npoints + 3) {
+                                               if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
+                                                       s_append_asprintf(errbuf, "line %d: the control point cannot be read for graphic object", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               j = (i - 3) * 3;
+                                               gp->points[j++] = dbuf[0];
+                                               gp->points[j++] = dbuf[1];
+                                               gp->points[j] = dbuf[2];
+                                       } else if (i == gp->npoints + 3) {
+                                               j = atoi(buf);
+                                               if (j < 0) {
+                                                       s_append_asprintf(errbuf, "line %d: the number of normals must be non-negative", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               if (j > 0)
+                                                       NewArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, j);
+                                       } else if (i >= gp->npoints + 3 && i < gp->npoints + gp->nnormals + 3) {
+                                               if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
+                                                       s_append_asprintf(errbuf, "line %d: the normal vector cannot be read for graphic object", lineNumber);
+                                                       goto err_exit;
+                                               }
+                                               j = (i - gp->npoints - 3) * 3;
+                                               gp->normals[j++] = dbuf[0];
+                                               gp->normals[j++] = dbuf[1];
+                                               gp->normals[j] = dbuf[2];
+                                       } else break;
+                                       i++;
+                               }
+                               MainView_insertGraphic(mp->mview, -1, gp);
+                               free(gp);
+                               if (buf[0] == '\n')
+                                       break;
+                       }
+                       continue;
+               } else if (strncmp(buf, "!:@", 3) == 0) {
+                       /*  Plug-in implemented in the ruby world  */
+                       Int stringLen;
+                       char *stringBuf, *returnString;
+                       i = strlen(buf);
+                       NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
+                       strcpy(stringBuf, buf);
+                       k = lineNumber;
+                       while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
+                               /*  The comment lines are _not_ skipped  */
+                               if (buf[0] == '\n')
+                                       break;
+                               j = strlen(buf);
+                               AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
+                               strncpy(stringBuf + i, buf, j);
+                               i += j;
+                       }
+                       if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
+                                                                                 "proc { |i| loadmbsf_plugin(i) rescue \"line #{i}: #{$i.to_s}\" }",
+                                                                                 stringBuf, k, &returnString) != 0) {
+                               s_append_asprintf(errbuf, "line %d: cannot invoke Ruby plugin", lineNumber);
+                               goto err_exit;
+                       } else if (returnString[0] != 0) {
+                               s_append_asprintf(errbuf, "%s", returnString);
+                               goto err_exit;
+                       }
+                       free(stringBuf);
+                       continue;
                }
                /*  Unknown sections are silently ignored  */
        }
@@ -1835,7 +1973,8 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                md_arena_set_molecule(mp->arena, mp);
 
        fclose(fp);
-       if (mp->mview != NULL) {
+
+/*     if (mp->mview != NULL) {
                if (mview_ibuf[0] != kUndefined)
                        mp->mview->showUnitCell = mview_ibuf[0];
                if (mview_ibuf[1] != kUndefined)
@@ -1877,6 +2016,7 @@ MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                                TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
                }
        }
+*/
 
        return 0;
 
@@ -4568,6 +4708,47 @@ MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
                }
                fprintf(fp, "\n");
        }
+
+       if (mp->mview != NULL && mp->mview->ngraphics > 0) {
+               MainViewGraphic *gp;
+               fprintf(fp, "!:graphics\n");
+               for (i = 0; i < mp->mview->ngraphics; i++) {
+                       gp = mp->mview->graphics + i;
+                       switch (gp->kind) {
+                               case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
+                               case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
+                               case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
+                               case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
+                               case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
+                               default: fprintf(fp, "unknown\n"); break;
+                       }
+                       fprintf(fp, "%d %d\n", gp->closed, gp->visible);
+                       fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
+                       fprintf(fp, "%d\n", gp->npoints);
+                       for (j = 0; j < gp->npoints; j++)
+                               fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
+                       fprintf(fp, "%d\n", gp->nnormals);
+                       for (j = 0; j < gp->nnormals; j++)
+                               fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
+               }
+               fprintf(fp, "\n");
+       }
+       
+       /*  Plug-in in the Ruby world  */
+       {
+               char *outMessage;
+               if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
+                                                                         "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
+                       if (outMessage[0] != 0) {
+                               if (strncmp(outMessage, "Plug-in", 7) == 0) {
+                                       s_append_asprintf(errbuf, "%s", outMessage);
+                               } else {
+                                       fprintf(fp, "%s\n", outMessage);
+                               }
+                       }
+                       free(outMessage);
+               }
+       }
        
        fclose(fp);
        return 0;
index b960b1f..72fde31 100644 (file)
@@ -4960,11 +4960,11 @@ s_Molecule_Equal(VALUE self, VALUE val)
 #pragma mark ------ Load/Save ------
 
 static void
-s_Molecule_RaiseOnLoadSave(int status, const char *msg, const char *fname)
+s_Molecule_RaiseOnLoadSave(int status, int isloading, const char *msg, const char *fname)
 {
        if (gLoadSaveErrorMessage != NULL) {
                MyAppCallback_setConsoleColor(1);
-               MyAppCallback_showScriptMessage("On loading %s:\n%s\n", fname, gLoadSaveErrorMessage);
+               MyAppCallback_showScriptMessage("On %s %s:\n%s\n", (isloading ? "loading" : "saving"), fname, gLoadSaveErrorMessage);
                MyAppCallback_setConsoleColor(0);
        }
        if (status != 0)
@@ -4990,7 +4990,7 @@ s_Molecule_Loadmbsf(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5017,13 +5017,13 @@ s_Molecule_Loadpsf(int argc, VALUE *argv, VALUE self)
        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;
 }
@@ -5048,7 +5048,7 @@ s_Molecule_Loadpdb(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5071,7 +5071,7 @@ s_Molecule_Loaddcd(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5094,7 +5094,7 @@ s_Molecule_Loadtep(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5117,7 +5117,7 @@ s_Molecule_Loadres(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5140,7 +5140,7 @@ s_Molecule_Loadfchk(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5165,7 +5165,7 @@ s_Molecule_Loaddat(int argc, VALUE *argv, VALUE self)
        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;   
 }
 
@@ -5185,7 +5185,7 @@ s_Molecule_Savembsf(VALUE self, VALUE fname)
        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;
 }
 
@@ -5205,7 +5205,7 @@ s_Molecule_Savepsf(VALUE self, VALUE fname)
        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;
 }
 
@@ -5225,7 +5225,7 @@ s_Molecule_Savepdb(VALUE self, VALUE fname)
        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;
 }
 
@@ -5245,7 +5245,7 @@ s_Molecule_Savedcd(VALUE self, VALUE fname)
        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;
 }
 
@@ -5320,7 +5320,7 @@ 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:
@@ -9623,55 +9623,185 @@ s_Molecule_NGraphics(VALUE self)
                rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
        return INT2NUM(mol->mview->ngraphics);
 }
-       
+
 /*
  *  call-seq:
- *     set_graphic_point(graphic_index, point_index, new_value) -> new_value
+ *     get_graphic_point(graphic_index, point_index) -> value
+ *     get_graphic_points(graphic_index) -> values
  *
- *  Change the point_index-th control point of graphic_index-th graphic object
+ *  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_SetGraphicPoint(VALUE self, VALUE gval, VALUE pval, VALUE nval)
+s_Molecule_GetGraphicPoint(int argc, VALUE *argv, VALUE self)
 {
        MainViewGraphic *gp;
     Molecule *mol;
-       int index;
+       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;
-       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");
+       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 {
-               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;
+               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;
+               }
+       }
+       MolActionCallback_registerUndo(mol, act);
+       MolActionRelease(act);          
        MoleculeCallback_notifyModification(mol, 0);
        return nval;
 }
 
 /*
  *  call-seq:
+ *     get_graphic_color(graphic_index) -> value
+ *
+ *  Get the color of graphic_index-th graphic object
+ */
+static VALUE
+s_Molecule_GetGraphicColor(VALUE self, VALUE gval)
+{
+       MainViewGraphic *gp;
+    Molecule *mol;
+       int index;
+    Data_Get_Struct(self, Molecule, mol);
+       if (mol->mview == NULL)
+               rb_raise(rb_eMolbyError, "this molecule has no associated graphic view");
+       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;
+       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]));
+}
+
+/*
+ *  call-seq:
  *     set_graphic_color(graphic_index, new_value) -> new_value
  *
  *  Change the color of graphic_index-th graphic object
@@ -9682,7 +9812,9 @@ s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
 {
        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");
@@ -9690,15 +9822,21 @@ s_Molecule_SetGraphicColor(VALUE self, VALUE gval, VALUE cval)
        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;
 }
@@ -11389,7 +11527,11 @@ Init_Molby(void)
        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);
index afa2215..ae72290 100755 (executable)
@@ -1211,4 +1211,14 @@ end_of_header
        self
   end
 
+  #  Plug-in for loading mbsf
+  def loadmbsf_plugin(s, lineno)
+    ""
+  end
+  
+  #  Plug-in for saving mbsf
+  def savembsf_plugin
+    ""
+  end
+  
 end