private IEnumerator calculateBonds()
        {
            MoleculeEvents.RaiseShowMessage("Calculating bonds. Please wait", false);
            yield return(null);

            Stopwatch watch = new Stopwatch();

            watch.Start();

            Thread thread = new Thread(() => {
                bonds = primaryStructure.GenerateBonds(Settings.NumberOfProcessorCores);
            });

            thread.Start();

            while (thread.IsAlive)
            {
                yield return(null);
            }

            watch.Stop();
            MoleculeEvents.RaiseShowMessage("Bonds calculated [" + watch.ElapsedMilliseconds + "ms]", false);
        }
        private int loadTrajectoryAtomCount(string trajectoryFile)
        {
            int count = 0;

            try {
                if (trajectoryFile.EndsWith(".xtc"))
                {
                    count = XTCTrajectoryParser.GetAtomCount(trajectoryFile);
                }
                else if (trajectoryFile.EndsWith(".dcd"))
                {
                    count = DCDTrajectoryParser.GetAtomCount(trajectoryFile);
                }
                else if (trajectoryFile.EndsWith(".gro"))
                {
                    count = GROTrajectoryParser.GetAtomCount(trajectoryFile);
                }
            }
            catch (FileParseException ex) {
                MoleculeEvents.RaiseShowMessage("Error Loading Trajectory File: " + ex.Message, true);
            }

            return(count);
        }
        private IEnumerator createModelAtomsByElement(MoleculeRenderSettings renderSettings, PrimaryStructureFrame frame, int meshQuality)
        {
            Quaternion atomOrientation = Quaternion.Euler(45, 45, 45);

            // generate combined meshes (i.e single GameObject) for atoms with same element/colour
            Dictionary <Color, List <Matrix4x4> > mergeTransforms = new Dictionary <Color, List <Matrix4x4> >();

            Dictionary <int, Atom> atoms = primaryStructure.GetAtoms(renderSettings.ShowStandardResidues, renderSettings.ShowNonStandardResidues, renderSettings.EnabledElements, renderSettings.EnabledResidueNames, renderSettings.EnabledResidueIDs);

            foreach (KeyValuePair <int, Atom> item in atoms)
            {
                Atom atom = item.Value;

                Vector3 position;

                // if no frame use the base structure coordinates.
                if (frame == null)
                {
                    position = new Vector3(atom.Position.x, atom.Position.y, atom.Position.z);
                }
                else
                {
                    if (atom.Index >= frame.AtomCount)
                    {
                        MoleculeEvents.RaiseShowMessage("Atoms not found in frame record. Aborting frame render.", true);
                        yield break;
                    }
                    position = new Vector3(frame.Coords[atom.Index * 3], frame.Coords[(atom.Index * 3) + 1], frame.Coords[(atom.Index * 3) + 2]);
                }

                // flip coord system for Unity
                position.z *= -1;

                Color32?customColour = null;
                MolecularRepresentation?customRepresentation = null;

                if (renderSettings.CustomResidueRenderSettings != null && renderSettings.CustomResidueRenderSettings.ContainsKey(atom.ResidueID))
                {
                    ResidueRenderSettings residueSettings = renderSettings.CustomResidueRenderSettings[atom.ResidueID];

                    if (residueSettings != null)
                    {
                        // use the atom specific settings if available.
                        if (residueSettings.AtomSettings.ContainsKey(atom.Name))
                        {
                            AtomRenderSettings atomSettings = residueSettings.AtomSettings[atom.Name];

                            if (atomSettings.CustomColour)
                            {
                                customColour = atomSettings.AtomColour;
                            }

                            if (atomSettings.Representation != MolecularRepresentation.None)
                            {
                                customRepresentation = atomSettings.Representation;
                            }
                        }

                        // if we didn't get from atom specific settings then get from residue settings
                        if (customColour == null && residueSettings.ColourAtoms)
                        {
                            customColour = residueSettings.ResidueColour;
                        }

                        if (customRepresentation == null)
                        {
                            if (residueSettings.AtomRepresentation != MolecularRepresentation.None)
                            {
                                customRepresentation = residueSettings.AtomRepresentation;
                            }
                        }
                    }
                }

                Color32 atomColour = Color.white;

                if (customColour != null)
                {
                    atomColour = (Color)customColour;
                }
                else
                {
                    if (!MolecularConstants.CPKColors.TryGetValue(atom.Element.ToString(), out atomColour))
                    {
                        MolecularConstants.CPKColors.TryGetValue("Other", out atomColour);
                    }
                }

                float   atomDiameter = getAtomRadius(atom, renderSettings, customRepresentation) * 2;
                Vector3 scale        = new Vector3(atomDiameter, atomDiameter, atomDiameter);

                Matrix4x4 atomTransform = Matrix4x4.TRS(position, atomOrientation, scale);

                if (!mergeTransforms.ContainsKey(atomColour))
                {
                    mergeTransforms.Add(atomColour, new List <Matrix4x4>());
                }

                mergeTransforms[atomColour].Add(atomTransform);
            }


            // create the meshes by colour
            GameObject prefab = AtomPrefabs[meshQuality];
            GameObject parent = new GameObject("CombinedMeshParent");

            parent.SetActive(false);

            foreach (KeyValuePair <Color, List <Matrix4x4> > item in mergeTransforms)
            {
                yield return(StartCoroutine(meshBuilder.CombinedMesh(prefab, item.Value.ToArray(), item.Key, parent)));
            }

            parent.transform.SetParent(AtomParent.transform, false);

            yield break;
        }
        public IEnumerator Render(int meshQuality)
        {
            // if currently rendering, then store render request
            if (rendering)
            {
                awaitingMeshQuality = meshQuality;
                awaitingRender      = true;
                yield break;
            }

            rendering = true;

            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();

            // primary structure render

            PrimaryStructureFrame frame = null;

            if (PrimaryStructureTrajectory != null && frameNumber != null)
            {
                frame = PrimaryStructureTrajectory.GetFrame((int)frameNumber);
            }

            // We use a clone of the render settings so any settings updates dont interfere with the builds
            MoleculeRenderSettings renderSettingsClone = renderSettings.Clone();

            yield return(StartCoroutine(primaryStructureRenderer.RenderStructure(renderSettingsClone, frame, meshQuality)));

            // secondary structure render

            SecondaryStructure secondaryStructureToBuild = null;

            if (secondaryStructureTrajectory != null && frameNumber != null && buildSecondaryStructureTrajectory)
            {
                string loadException = null;

                Thread thread = new Thread(() => {
                    try {
                        secondaryStructureToBuild = secondaryStructureTrajectory.GetStructure((int)frameNumber);
                    }
                    catch (FileParseException ex) {
                        loadException = ex.Message;
                    }
                });

                thread.Start();

                while (thread.IsAlive)
                {
                    yield return(null);
                }

                if (loadException != null)
                {
                    MoleculeEvents.RaiseShowMessage(loadException + " - Aborting trajectory secondary structure builds.", true);
                    buildSecondaryStructureTrajectory = false;
                }
            }
            else
            {
                secondaryStructureToBuild = secondaryStructure;
            }

            yield return(StartCoroutine(secondaryStructureRenderer.RenderStructure(renderSettingsClone, frame, secondaryStructureToBuild)));

            primaryStructureRenderer.ShowStructure();
            secondaryStructureRenderer.ShowStructure();

            // simulation box render

            if (renderSettingsClone.ShowSimulationBox)
            {
                moleculeBox.gameObject.SetActive(true);
                BoundingBox box = boundingBox;

                if (renderSettingsClone.CalculateBoxEveryFrame && frame != null)
                {
                    box = new BoundingBox(frame, true);
                }

                moleculeBox.Build(box);
            }
            else
            {
                moleculeBox.gameObject.SetActive(false);
            }

            //Cleanup.ForeceGC();

            rendering = false;
            watch.Stop();
            if (Settings.DebugMessages)
            {
                //console.BannerBuildTime = watch.ElapsedMilliseconds.ToString();
            }

            //UnityEngine.Debug.Log("Ending model build. Elapsed time [" + watch.ElapsedMilliseconds.ToString() + "]");
            yield break;
        }
        // Loads and renders a molecule using a structure file path and render settings
        public IEnumerator LoadMoleculeStructure(int moleculeID, string filePath, MoleculeRenderSettings settings)
        {
            if (molecules.ContainsKey(moleculeID))
            {
                MoleculeEvents.RaiseShowMessage("Error Loading Molecule: already loaded", true);
                MoleculeEvents.RaiseOnMoleculeLoadFailed(moleculeID);
                yield break;
            }

            if (loadingStructure)
            {
                MoleculeEvents.RaiseShowMessage("Can't Load Molecule: another molecule currently loading", true);
                MoleculeEvents.RaiseOnMoleculeLoadFailed(moleculeID);
                yield break;
            }

            var watch = System.Diagnostics.Stopwatch.StartNew();

            MoleculeEvents.RaiseShowMessage("Loading Structure File: " + filePath, false);

            loadingStructure = true;
            int oldAtomMeshQuality = generalSettings.MeshQuality;

            Debug.Log("Loading structure");

            PrimaryStructure primaryStructure = null;
            string           loadException    = null;

            Thread thread = new Thread(() => {
                try {
                    if (filePath.EndsWith(".gro"))
                    {
                        primaryStructure = GROStructureParser.GetStructure(filePath);
                    }
                    else if (filePath.EndsWith(".xyz"))
                    {
                        primaryStructure = XYZStructureParser.GetStructure(filePath);
                    }
                    else if (filePath.EndsWith(".pdb"))
                    {
                        primaryStructure = PDBStructureParser.GetPrimaryStructure(filePath);
                    }
                }
                catch (FileParseException ex) {
                    loadException = ex.Message;
                }
            });

            thread.Start();

            while (thread.IsAlive)
            {
                yield return(null);
            }

            Debug.Log("Finished Loading structure");

            if (loadException != null)
            {
                Debug.Log("Error Loading Structure File: " + loadException);
                MoleculeEvents.RaiseShowMessage("Error Loading Structure File: " + loadException, true);
                MoleculeEvents.RaiseOnMoleculeLoadFailed(moleculeID);
                loadingStructure = false;
                yield break;
            }

            watch.Stop();
            MoleculeEvents.RaiseShowMessage("Structure File Load Complete [" + watch.ElapsedMilliseconds + "ms]", false);
            yield return(null);

            if (primaryStructure != null)
            {
                GameObject moleculeGO = GameObject.Instantiate(MoleculePrefab);
                moleculeGO.transform.parent = this.transform;
                moleculeGO.SetActive(true);

                Molecule molecule = moleculeGO.GetComponent <Molecule>();
                molecule.Initialise(moleculeID, primaryStructure, settings);
                molecule.AutoRotateSpeed = generalSettings.AutoRotateSpeed;
                molecule.SetInputSensitivity(generalSettings.MoleculeInputSensitivity);
                molecule.SetSpaceNavigatorControlEnabled(generalSettings.SpaceNavigatorMoleculeControlEnabled);
                molecules.Add(moleculeID, molecule);

                // check to see if the meshQuality needs to change given the new primary structure
                updateMeshQuality();

                molecule.SetRenderSettings(settings);
                yield return(StartCoroutine(molecule.Render(generalSettings.MeshQuality)));

                MoleculeEvents.RaiseMoleculeLoaded(moleculeID, Path.GetFileName(filePath), primaryStructure);
            }

            if (oldAtomMeshQuality != generalSettings.MeshQuality)
            {
                reRenderMolecules();
            }

            SaveCurrentMoleculeTransformAsDefault(moleculeID);

            loadingStructure = false;
        }
        // Loads a molecule trajectory. Will only work if molecule is already loaded
        public IEnumerator LoadMoleculeTrajectory(int moleculeID, string filePath)
        {
            if (!molecules.ContainsKey(moleculeID))
            {
                MoleculeEvents.RaiseShowMessage("Can't load molecule trajectory. No molecule found.", true);
                yield break;
            }

            if (loadingTrajectory)
            {
                MoleculeEvents.RaiseShowMessage("Can't Load Trajectory: another trajectory currently loading", true);
                yield break;
            }

            PrimaryStructure primaryStructure = molecules[moleculeID].PrimaryStructure;
            int atomCount = loadTrajectoryAtomCount(filePath);

            if (atomCount != primaryStructure.AtomCount())
            {
                MoleculeEvents.RaiseShowMessage("Trajectory atom count [" + atomCount + " doesn't match loaded structure atom count [" + primaryStructure.AtomCount() + "]", true);
                yield break;
            }

            MoleculeEvents.RaiseShowMessage("Loading trajectory. Please wait", false);

            loadingTrajectory = true;

            PrimaryStructureTrajectory trajectory = null;
            string loadException = null;

            Thread thread = new Thread(() => {
                try {
                    int startFrame     = 0;
                    int frameFrequency = 1;
                    int frameCount     = Settings.MaxTrajectoryFrames;

                    if (filePath.EndsWith(".xtc"))
                    {
                        trajectory = XTCTrajectoryParser.GetTrajectory(filePath, startFrame, frameCount, frameFrequency);
                    }
                    else if (filePath.EndsWith(".dcd"))
                    {
                        trajectory = DCDTrajectoryParser.GetTrajectory(filePath, startFrame, frameCount, frameFrequency);
                    }
                    else if (filePath.EndsWith(".gro"))
                    {
                        trajectory = GROTrajectoryParser.GetTrajectory(filePath, startFrame, frameCount, frameFrequency);
                    }
                }
                catch (FileParseException ex) {
                    loadException = ex.Message;
                }
            });

            thread.Start();

            while (thread.IsAlive)
            {
                yield return(null);
            }

            if (loadException != null)
            {
                MoleculeEvents.RaiseShowMessage("Error Loading Trajectory File: " + loadException, true);
                loadingTrajectory = false;
                yield break;
            }

            if (trajectory != null)
            {
                molecules[moleculeID].SetTrajectory(trajectory);
                MoleculeEvents.RaiseTrajectoryLoaded(moleculeID, filePath, trajectory.FrameCount());
            }

            loadingTrajectory = false;
        }