parsers_XYZ.ts

import { ParserOptionsSpec } from './ParserOptionsSpec';

import { Matrix3 } from "../WebGL";
import { assignBonds } from "./utils/assignBonds";

/**
 * Read an XYZ file from str and return result
 * 
 * @param {string} str
 * @param {ParserOptionsSpec} options
 * @category Parsers
*/

export function XYZ(str: string, options: ParserOptionsSpec) {
  options = options || {};
  var atoms: any[][] & Record<string, any> = [[]];
  var assignbonds =
    options.assignBonds === undefined ? true : options.assignBonds;
  var lines = str.split(/\r?\n|\r/);
  while (lines.length > 0) {
    if (lines.length < 3) break;
    var atomCount = parseInt(lines[0]);
    if (isNaN(atomCount) || atomCount <= 0) break;
    if (lines.length < atomCount + 2) break;

    var lattice_re = /Lattice\s*=\s*["\{\}]([^"\{\}]+)["\{\}]\s*/gi;
    var lattice_match = lattice_re.exec(lines[1]);
    if (lattice_match != null && lattice_match.length > 1) {
      var lattice = new Float32Array(lattice_match[1].split(/\s+/) as any);
      var matrix = new Matrix3(
        lattice[0],
        lattice[3],
        lattice[6],
        lattice[1],
        lattice[4],
        lattice[7],
        lattice[2],
        lattice[5],
        lattice[8]
      );
      atoms.modelData = [{ cryst: { matrix: matrix } }];
    }

    var offset = 2;
    var start = atoms[atoms.length - 1].length;
    var end = start + atomCount;
    for (var i = start; i < end; i++) {
      var line = lines[offset++];
      var tokens = line.replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
      var atom: Record<string, any> = {};
      atom.serial = i;
      var elem = tokens[0];
      atom.atom = atom.elem =
        elem[0].toUpperCase() + elem.substring(1, 2).toLowerCase();
      atom.x = parseFloat(tokens[1]);
      atom.y = parseFloat(tokens[2]);
      atom.z = parseFloat(tokens[3]);
      atom.hetflag = true;
      atom.bonds = [];
      atom.bondOrder = [];
      atom.properties = {};
      atoms[atoms.length - 1][i] = atom;
      if (tokens.length >= 7) {
        atom.dx = parseFloat(tokens[4]);
        atom.dy = parseFloat(tokens[5]);
        atom.dz = parseFloat(tokens[6]);
      }
    }

    if (options.multimodel) {
      atoms.push([]);
      lines.splice(0, offset);
    } else {
      break;
    }
  }

  if (assignbonds) {
    for (let i = 0; i < atoms.length; i++) {
      assignBonds(atoms[i], options);
    }
  }

  if (options.onemol) {
    var temp = atoms;
    atoms = [];
    atoms.push(temp[0]);
    for (let i = 1; i < temp.length; i++) {
      let offset = atoms[0].length;
      for (let j = 0; j < temp[i].length; j++) {
        let a = temp[i][j];
        for (let k = 0; k < a.bonds.length; k++) {
          a.bonds[k] = a.bonds[k] + offset;
        }
        a.index = atoms[0].length;
        a.serial = atoms[0].length;
        atoms[0].push(a);
      }
    }
  }

  return atoms;
}