Esempio n. 1
0
        public override IEnumerable <Selection> Select(IStructure structure)
        {
            List <Selection> results = new List <Selection>();

            for (int chainIndex = 0; chainIndex < structure.Count; chainIndex++)
            {
                if (chainIndex == ChainIndex)
                {
                    continue;
                }

                IChain         chain           = structure[chainIndex];
                List <SSBlock> chainSelections = SecondaryStructure.GetPhiPsiSSBlocks(chain, MinLength);

                if (IncludeAdjacentLoops)
                {
                    // Extend the SSBlock ranges to include everything up to the neighboring blocks
                    List <SSBlock> extendedChainSelections = new List <SSBlock>();
                    for (int selectionIndex = 0; selectionIndex < chainSelections.Count; selectionIndex++)
                    {
                        int start = selectionIndex == 0 ? 0 : chainSelections[selectionIndex - 1].End + 1;
                        int end   = selectionIndex == chainSelections.Count - 1 ? chain.Count - 1 : chainSelections[selectionIndex + 1].Start - 1;
                        extendedChainSelections.Add(new SSBlock(chainSelections[selectionIndex].SS, start, end));
                    }
                    chainSelections = extendedChainSelections;
                }

                if (SkipCountC != null && SkipCountC > 0)
                {
                    int firstSkipIndex = Math.Max(0, chainSelections.Count - (int)SkipCountC);
                    chainSelections.RemoveRange(firstSkipIndex, chainSelections.Count - firstSkipIndex);
                }

                if (SkipCountN != null && SkipCountN > 0)
                {
                    int skipCount = Math.Min((int)SkipCountN, chainSelections.Count);
                    chainSelections.RemoveRange(0, skipCount);
                }

                // Convert the computed SS block ranges to an AA selection and add that to list
                results.AddRange(chainSelections.Select(block => new Selection(chain[block.Start, block.End])));
            }

            return(results);
        }
        public void Initialise(int id, PrimaryStructure primaryStructure, MoleculeRenderSettings renderSettings)
        {
            if (primaryStructure == null)
            {
                return;
            }

            this.ID = id;
            this.PrimaryStructure = primaryStructure;
            this.renderSettings   = renderSettings;
            this.frameNumber      = null;

            try {
                secondaryStructure = SecondaryStructure.CreateFromPrimaryStructure(primaryStructure, Settings.StrideExecutablePath, Settings.TmpFilePath);
            }
            catch (Exception ex) {
                Debug.Log("Error Parsing Secondary Structure from Structure File: " + ex.Message);
                buildSecondaryStructureTrajectory = false;
            }

            moleculeBox.gameObject.SetActive(renderSettings.ShowSimulationBox);
            boundingBox = new BoundingBox(primaryStructure, true);
            moleculeRender.transform.position = new Vector3(-1 * boundingBox.Centre.x, -1 * boundingBox.Centre.y, -1 * boundingBox.Centre.z);

            this.transform.position = new Vector3(boundingBox.Centre.x, boundingBox.Centre.y, boundingBox.Centre.z);

            if (renderSettings.ShowSimulationBox)
            {
                moleculeBox.Build(boundingBox);
            }

            primaryStructureRenderer.Initialise(primaryStructure);
            secondaryStructureRenderer.Initialise(primaryStructure);

            moleculeInput.enabled = false;
            autoRotateEnabled     = false;
            AutoRotateSpeed       = 0f;
        }
Esempio n. 3
0
    public void ParseFile()
    {
        topY              = -99999; bottomY = 99999; leftX = 99999; rightX = -99999; frontZ = 99999; backZ = -99999;
        startTime         = Time.realtimeSinceStartup;
        ConsoleText.text += string.Format(" Reading in DSSP file...");
        DSSPfile          = GetComponent <HttpTest>().outputfile;
        string pattern = @"([0-9]+)\s+(\S+)\s+([A-Z]+)\s+([A-Z]+)..(.)...........(....)(...)(...)(............)(...........)(...........)(...........)(........)(......)(......)(......)(......)\s+(\S+)\s+(\S+)\s+(\S+..)";

        string getnumAA = @"([0-9]+)\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+TOTAL NUMBER OF RESIDUES";

        string newmolpattern = @"!*             0   0    0      0, 0.0     0, 0.0     0, 0.0     0, 0.0   0.000 360.0 360.0 360.0 360.0    0.0    0.0    0.0";

        string header = @"#  RESIDUE AA STRUCTURE BP1 BP2  ACC     N-H-->O    O-->H-N    N-H-->O    O-->H-N    TCO  KAPPA ALPHA  PHI   PSI    X-CA   Y-CA   Z-CA            CHAIN AUTHCHAIN";

        // get total num of amino acids in the protein complex
        Regex getnumAAr = new Regex(getnumAA);
        int   numaa     = Convert.ToInt32(getnumAAr.Match(DSSPfile).Groups[1].ToString());

        ConsoleText.text += string.Format("\n Total number of amino acids: {0}", numaa);

        if (Regex.Split(Regex.Split(DSSPfile, header)[1], newmolpattern) == null)
        {
            ConsoleText.text += string.Format("\n string[] molecules is null.");
        }

        string[] molecules = Regex.Split(Regex.Split(DSSPfile, header)[1], newmolpattern);
        int      totmolnum = molecules.Length;                                                         // index of molecule. total num of molecules in a protein complex

        List <SecondaryStructure[]> MoleculesSecondaryAssignments = new List <SecondaryStructure[]>(); // We want to make lists of the above vectors per molecule
        List <Vector3[]>            MoleculesCAPoints             = new List <Vector3[]>();

        foreach (string molecule in molecules)
        {
            int te = 0; // temp
            SecondaryStructure[] structure = new SecondaryStructure[Regex.Matches(molecule, pattern).Count];
            Vector3[]            points    = new Vector3[Regex.Matches(molecule, pattern).Count];

            if (Regex.Matches(molecule, pattern) == null)
            {
                ConsoleText.text += string.Format("\n Error reading DSSP file. Regex pattern returned no matches. Matches is null.");
            }

            foreach (Match matches in Regex.Matches(molecule, pattern))
            {
                structure[te].structure = matches.Groups[5].ToString();

                points[te] = new Vector3(float.Parse(matches.Groups[18].ToString()),
                                         float.Parse(matches.Groups[19].ToString()),
                                         float.Parse(matches.Groups[20].ToString()));
                te++;
            }

            MoleculesSecondaryAssignments.Add(structure);
            MoleculesCAPoints.Add(points);
        }

        GameObject ThisFile = new GameObject();

        ThisFile.name                    = string.Format("File {0}", GetComponent <Load>().FileNumber);
        ThisFile.transform.parent        = GameObject.Find("MoleculeCollection").transform;
        ThisFile.transform.localPosition = Vector3.zero;
        ThisFile.transform.localScale    = Vector3.one;
        GameObject RibbonCollection = new GameObject();

        RibbonCollection.name                    = "RibbonCollection";
        RibbonCollection.transform.parent        = ThisFile.transform;
        RibbonCollection.transform.localPosition = Vector3.zero;
        RibbonCollection.transform.localScale    = Vector3.one;
        // TODO : Put this in script Ribbon.cs as "DrawRibbon(Vector3 points)"
        GameObject ThisPDB = new GameObject();

        ThisPDB.name                 = string.Format("PDB{0}", GetComponent <Load>().FileNumber);
        ThisPDB.transform.parent     = RibbonCollection.transform;
        ThisPDB.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
        //ThisPDB.transform.localPosition = new Vector3(0.75f, 0, 0) * (numPDB - 1);

        GameObject MoleculeHolder = new GameObject();

        MoleculeHolder.transform.parent = ThisPDB.transform;
        MeshFilter[] meshFilters = new MeshFilter[totmolnum];
        ConsoleText.text += string.Format("\n Total number of molecules: {0}.", totmolnum);
        Vector3    offset = new Vector3(0, 0f, 0f);
        GameObject parent = GameObject.Find("Hologram Collection"); // TODO: Remove find

        for (int tem = 0; tem < totmolnum; tem++)
        {
            // Make a new vector that has more points in it so it is smoother
            // The higher the second number, the more points and thus smoother lines. Changes smoothness exponentially relative to number of amino acids.
            Vector3[] newPoints = LineSmoother.SmoothLine(MoleculesCAPoints[tem], 0.8f * (float)Math.Exp(-10f / (float)numaa));
            if (newPoints == null)
            {
                ConsoleText.text += string.Format("\n Error initializing smoothing array. NewPoints is null.");
            }

            // Second argument adjusts the smoothness. Smaller = smoother
            // TODO: Second argument can be adjusted for changing quality using slider


            // TODO: These three parameters can be adjustable with sliders
            float lineWidth  = 0.006f;
            float rescale    = 100f;
            int   numofsides = 12;

            int[]     triangles = new int[6 * numofsides * newPoints.Length];
            Vector3[] quad      = new Vector3[newPoints.Length * numofsides];
            Color[]   colors    = new Color[quad.Length];

            float a = lineWidth;
            float b = lineWidth / 2.5f;

            ConsoleText.text += string.Format("\n Initialized arrays and variables for molecule {0}...", tem);

            //foreach (Vector3 point in newPoints)
            foreach (Vector3 point in newPoints)
            {
                tiltFactor = 30;
                int i                = Array.IndexOf(newPoints, point);
                int totnumofsides    = i * numofsides;
                int totnumofvertices = totnumofsides * 6;
                int div              = Convert.ToInt32(Math.Floor(i / ((float)newPoints.Length / (float)MoleculesCAPoints[tem].Length)));

                // Adds color based on secondary structure assignment
                for (int z = 0; z < numofsides; z++)
                {
                    colors[totnumofsides + z] = Colors(MoleculesSecondaryAssignments[tem][div].structure);
                }
            }

            Parallel.ForEach(newPoints, point =>
            {
                tiltFactor           = 30;
                int i                = Array.IndexOf(newPoints, point);
                int totnumofsides    = i * numofsides;
                int totnumofvertices = totnumofsides * 6;

                int div = Convert.ToInt32(Math.Floor(i / ((float)newPoints.Length / (float)MoleculesCAPoints[tem].Length)));



                // for padding on the z axis:
                if (totnumofsides < (newPoints.Length - 2) * 6 && Mathf.Abs(point.z - newPoints[i + 1].z) > Mathf.Abs(point.x - newPoints[i + 1].x) && Mathf.Abs(point.z - newPoints[i + 1].z) > Mathf.Abs(point.y - newPoints[i + 1].y))
                {
                    tiltFactor = 45;
                }

                // Makes an oval
                // TODO: Find a way to fix thinness of certain axes (the padding on z axis method right above doesn't entirely work)

                for (int side = 0; side < numofsides; side++)
                {
                    float angle = (side * (360f / numofsides)) * Mathf.Deg2Rad;
                    float x     = ((float)a * b) / (float)Mathf.Sqrt(((float)Math.Pow(b, 2) + ((float)Math.Pow(a, 2) * (float)Math.Pow(Mathf.Tan(angle), 2))));
                    float y     = ((float)a * b) / (float)Mathf.Sqrt((float)Mathf.Pow(a, 2) + ((float)Mathf.Pow(b, 2) / (float)Mathf.Pow(Mathf.Tan(angle), 2)));

                    if (side * (360f / numofsides) > 90 && side * (360f / numofsides) < 270)
                    {
                        x = -1 * x;
                    }

                    if (side * (360f / numofsides) > 180 && side * (360f / numofsides) < 360)
                    {
                        y = -1 * y;
                    }

                    // Determine the max/min vertices for resizing and positioning later
                    if ((point / rescale).y + y > topY)
                    {
                        topY = (point / rescale).y + y;
                    }
                    if ((point / rescale).y + y < bottomY)
                    {
                        bottomY = (point / rescale).y + y;
                    }
                    if ((point / rescale).x + x > rightX)
                    {
                        rightX = (point / rescale).x + x;
                    }
                    if ((point / rescale).x + x < leftX)
                    {
                        leftX = (point / rescale).x + x;
                    }
                    if ((point / rescale).z > backZ)
                    {
                        backZ = (point / rescale).z;
                    }
                    if ((point / rescale).z < frontZ)
                    {
                        frontZ = (point / rescale).z;
                    }

                    quad[totnumofsides + side] = (point / rescale) + offset + new Vector3(x, y, 0);
                }

                // Creates an array for the triangles
                if (i < (newPoints.Length - 1))
                {
                    for (int p = 0; p < totnumofsides; p++)
                    {
                        triangles[(p * 6) + 0] = p + 0;
                        triangles[(p * 6) + 1] = p + numofsides;
                        triangles[(p * 6) + 2] = p + 1;
                        triangles[(p * 6) + 3] = p + 1;
                        triangles[(p * 6) + 4] = p + numofsides;
                        triangles[(p * 6) + 5] = p + numofsides + 1;
                    }
                }
            }
                             );

            // Draw the molecule
            GameObject Protein = new GameObject();

            Protein.transform.parent = MoleculeHolder.transform; // remove find
            Protein.name             = string.Format("PDB.{0}.molecule.{1}", GetComponent <Load>().FileNumber, tem);

            // some sanity checks
            if (GameObject.Find(string.Format("PDB.{0}.molecule.{1}", GetComponent <Load>().FileNumber, tem)) == null)
            {
                ConsoleText.text += string.Format("\n Molecule {0} GameObject does not exist.", tem);
            }
            if (quad == null)
            {
                ConsoleText.text += string.Format("\nQuad is null");
            }
            if (triangles == null)
            {
                ConsoleText.text += string.Format("\n Triangles is null.");
            }

            Protein.AddComponent <MeshFilter>();
            Protein.AddComponent <MeshRenderer>();

            //Protein.GetComponent<MeshRenderer>().material = new Material(Shader.Find("VertexColorLightmaps"));
            Protein.GetComponent <MeshFilter>().mesh.vertices  = quad;
            Protein.GetComponent <MeshFilter>().mesh.triangles = triangles;
            Protein.GetComponent <MeshFilter>().mesh.colors    = colors;

            Protein.GetComponent <MeshRenderer>().material = new Material(Shader.Find("Custom/SpecularHighlight"));

            Protein.GetComponent <MeshFilter>().mesh.RecalculateNormals();

            //meshFilters[tem] = Protein.GetComponent<MeshFilter>();
        }

        /*
         *      ThisPDB.AddComponent<MeshCollider>();
         *      ThisPDB.AddComponent<MeshFilter>();
         *      CombineInstance[] combine = new CombineInstance[meshFilters.Length];
         *      int inde = 0;
         *      while (inde < meshFilters.Length)
         *      {
         *              combine[inde].mesh = meshFilters[inde].sharedMesh;
         *              combine[inde].transform = meshFilters[inde].transform.localToWorldMatrix;
         *              //meshFilters[inde].gameObject.SetActive(false);
         *              inde++;
         *      }
         *      ThisPDB.transform.GetComponent<MeshFilter>().mesh = new Mesh();
         *      ThisPDB.transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
         *      ThisPDB.transform.gameObject.SetActive(true);
         *      ThisPDB.transform.GetComponent<MeshCollider>().sharedMesh = ThisPDB.transform.GetComponent<MeshFilter>().mesh;
         *      ThisPDB.AddComponent<MeshRenderer>().material = new Material(Shader.Find("Custom/SpecularHighlight"));
         */
        MoleculeHolder.transform.localPosition -= new Vector3((rightX + leftX) / 2f, (topY + bottomY) / 2f, (backZ + frontZ) / 2f);
        ThisPDB.transform.localPosition         = Vector3.zero;

        //ThisPDB.AddComponent<BoxCollider>().size = new Vector3(0.5f, 0.5f, 0.5f);
        //ThisPDB.AddComponent<MeshRenderer>().material = GameObject.Find("MoleculeCollection").GetComponent<MeshRenderer>().material;



        if (Math.Abs(topY - bottomY) >= Math.Abs(rightX - leftX))
        {
            if (Math.Abs(backZ - frontZ) >= Math.Abs(topY - bottomY))
            {
                MoleculeHolder.transform.localScale /= Math.Abs(backZ - frontZ) / .3f; // instead of .48f make this localscale of moelculecolelctin
            }
            else
            {
                MoleculeHolder.transform.localScale /= Math.Abs(topY - bottomY) / .3f;
            }
        }
        else
        {
            MoleculeHolder.transform.localScale /= Math.Abs(rightX - leftX) / .3f;
        }

        // change back to interpolated strings
        ConsoleText.text += string.Format("\n Cartoon structure drawn for PDB #{0}.", GetComponent <Load>().FileNumber);
        finishTime        = Time.realtimeSinceStartup;
        ConsoleText.text += string.Format("\n Time taken to draw: {0} seconds.\n", (finishTime - startTime));
        Debug.Log($"Time to draw protein: {finishTime - startTime}");

        //ThisPDB.AddComponent<BoxCollider>().size = new Vector3(0.48f / ThisPDB.transform.localScale.x, 0.48f / ThisPDB.transform.localScale.x, 0.48f / ThisPDB.transform.localScale.x);
        //GameObject.Find("RibbonCollection").GetComponent<BoxCollider>().enabled = true;
        //MoleculeHolder.transform.localScale = new Vector3(3, 3, 3);

        RibbonCollection.AddComponent <BoundingBoxRig>().ScaleHandleMaterial  = BoundingBoxHandle;
        RibbonCollection.GetComponent <BoundingBoxRig>().RotateHandleMaterial = BoundingBoxHandle;
        RibbonCollection.GetComponent <BoundingBoxRig>().InteractingMaterial  = BoundingBoxHandleGrabbed;
        RibbonCollection.GetComponent <BoundingBoxRig>().BoundingBoxPrefab    = BoundingBoxBasic;
        RibbonCollection.GetComponent <BoundingBoxRig>().appBarPrefab         = AppBar;
        RibbonCollection.AddComponent <MixedRealityToolkit.InputModule.Utilities.Interations.TwoHandManipulatable>().BoundingBoxPrefab = BoundingBoxBasic;

        RibbonCollection.GetComponent <MixedRealityToolkit.InputModule.Utilities.Interations.TwoHandManipulatable>().SetManipulationMode(MixedRealityToolkit.InputModule.Utilities.Interations.TwoHandManipulatable.TwoHandedManipulation.MoveRotateScale);

        ThisPDB.AddComponent <MeshFilter>().mesh = GameObject.Find("MoleculeCollection").GetComponent <MeshFilter>().mesh;

        ThisPDB.AddComponent <MeshCollider>().sharedMesh = ThisPDB.GetComponent <MeshFilter>().mesh;
        ThisPDB.AddComponent <MeshRenderer>().material   = GameObject.Find("MoleculeCollection").GetComponent <MeshRenderer>().material;
        ThisPDB.GetComponent <MeshRenderer>().enabled    = false;
        GetComponent <Load>().FileNumber++;
    }
        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;
        }
        private IEnumerator createSecondaryStructure(MoleculeRenderSettings settings, PrimaryStructureFrame frame, SecondaryStructure secondaryStructure)
        {
            for (int i = 0; i < primaryStructure.Chains().Count; i++)
            {
                Chain chain = primaryStructure.Chains()[i];

                if (chain.ResidueType != StandardResidue.AminoAcid)
                {
                    // UnityEngine.Debug.Log("Skipping secondary strucure. Non protein structures not currently supported.");
                    continue;
                }

                if (chain.MainChainResidues.Count < 2)
                {
                    // UnityEngine.Debug.Log("Skipping secondary strucure. Protein structure doesn't have enough residues for mesh.");
                    continue;
                }

                Mesh structureMesh = BuildStructureMesh(settings, chain, frame, secondaryStructure);

                GameObject structure = (GameObject)Instantiate(SecondaryStructurePrefab);
                structure.GetComponent <MeshFilter>().sharedMesh = structureMesh;

                structure.SetActive(false);
                structure.transform.SetParent(StructureParent.transform, false);

                yield return(null);
            }
        }
        // RenderStructure and ShowStructure are separate to allow the primary and secondary structures to be prerendered and then
        // both turned on at the same time. Without this, the show of the structures is staggered and shows some level of flickering
        public IEnumerator RenderStructure(MoleculeRenderSettings settings, PrimaryStructureFrame frame, SecondaryStructure secondaryStructure)
        {
            if (!initialised)
            {
                yield break;
            }

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

            yield return(null);

            storeExistingStructure();

            // create mesh
            if (secondaryStructure != null && settings.ShowSecondaryStructure)
            {
                yield return(StartCoroutine(createSecondaryStructure(settings, frame, secondaryStructure)));
            }

            watch.Stop();
        }
        private Mesh BuildStructureMesh(MoleculeRenderSettings renderSettings, Chain chain, PrimaryStructureFrame frame, SecondaryStructure secondaryStructure)
        {
            Mesh  structureMesh = null;
            int   interpolation = 20;
            int   resolution    = 6; // should be in config
            float radius        = 0.015f;

            List <DynamicMeshNode> nodes = new List <DynamicMeshNode>();

            Vector3 lastPosition            = Vector3.zero;
            Vector3 lastNormal              = Vector3.zero;
            Vector3 averagedNormal          = Vector3.zero;
            SecondaryStructureType lastType = SecondaryStructureType.Coil;

            for (int i = 0; i < chain.MainChainResidues.Count; i++)
            {
                DynamicMeshNode node = new DynamicMeshNode();

                Residue residue = chain.MainChainResidues[i];

                // check if residue mainchain information is complete. Ignore if not
                if (residue.AlphaCarbon == null || residue.CarbonylCarbon == null || residue.CarbonylOxygen == null)
                {
                    continue;
                }

                // set position
                Atom atom = residue.AlphaCarbon;

                // if no frame number use the base structure coordinates.
                Vector3 position;
                if (frame == null)
                {
                    position = new Vector3(atom.Position.x, atom.Position.y, atom.Position.z);
                }
                else
                {
                    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;

                node.Position = position;

                SecondaryStructureInfomation structureInformation = secondaryStructure.GetStructureInformation(residue.Index);

                Residue nextResidue = null;
                if (i + 1 < chain.MainChainResidues.Count)
                {
                    nextResidue = chain.MainChainResidues[i + 1];
                }

                SecondaryStructureInfomation nextResidueStructureInfo = null;
                if (nextResidue != null)
                {
                    nextResidueStructureInfo = secondaryStructure.GetStructureInformation(nextResidue.Index);
                }

                // store the node type
                if (structureInformation != null)
                {
                    if (renderSettings.ShowHelices &&
                        (structureInformation.type == SecondaryStructureType.ThreeHelix ||
                         structureInformation.type == SecondaryStructureType.AlphaHelix ||
                         structureInformation.type == SecondaryStructureType.FiveHelix))
                    {
                        node.Type = DynamicMeshNodeType.SpiralRibbon;
                    }
                    else if (renderSettings.ShowSheets &&
                             structureInformation.type == SecondaryStructureType.BetaSheet)
                    {
                        if (nextResidue == null || (nextResidueStructureInfo != null && nextResidueStructureInfo.type != SecondaryStructureType.BetaSheet))
                        {
                            node.Type = DynamicMeshNodeType.RibbonHead;
                        }
                        else
                        {
                            node.Type = DynamicMeshNodeType.Ribbon;
                        }
                    }
                    else if (renderSettings.ShowTurns &&
                             structureInformation.type == SecondaryStructureType.Turn)
                    {
                        node.Type = DynamicMeshNodeType.LargeTube;
                    }
                    else
                    {
                        node.Type = DynamicMeshNodeType.Tube;
                    }


                    // calculate and store the node color

                    bool foundColour = false;

                    if (renderSettings.CustomResidueRenderSettings != null && renderSettings.CustomResidueRenderSettings.ContainsKey(residue.ID))
                    {
                        ResidueRenderSettings residueRenderSettings = renderSettings.CustomResidueRenderSettings[residue.ID];

                        if (residueRenderSettings != null && residueRenderSettings.ColourSecondaryStructure)
                        {
                            node.VertexColor = residueRenderSettings.ResidueColour;
                            foundColour      = true;
                        }
                    }

                    if (foundColour == false)
                    {
                        switch (structureInformation.type)
                        {
                        case SecondaryStructureType.ThreeHelix:
                            node.VertexColor = Settings.ThreeHelixColour;
                            break;

                        case SecondaryStructureType.AlphaHelix:
                            node.VertexColor = Settings.AlphaHelixColour;
                            break;

                        case SecondaryStructureType.FiveHelix:
                            node.VertexColor = Settings.FiveHelixColour;
                            break;

                        case SecondaryStructureType.Turn:
                            node.VertexColor = Settings.TurnColour;
                            break;

                        case SecondaryStructureType.BetaSheet:
                            node.VertexColor = Settings.BetaSheetColour;
                            break;

                        case SecondaryStructureType.BetaBridge:
                            node.VertexColor = Settings.BetaBridgeColour;
                            break;

                        case SecondaryStructureType.Bend:
                            node.VertexColor = Settings.BendColour;
                            break;

                        case SecondaryStructureType.Coil:
                            node.VertexColor = Settings.CoilColour;
                            break;

                        default:
                            node.VertexColor = ErrorColor;
                            break;
                        }
                    }
                }
                else
                {
                    Debug.Log("*** Structure info null: assigning defaults");
                    node.Type        = DynamicMeshNodeType.Tube;
                    node.VertexColor = ErrorColor;
                }

                // determine the node rotation
                // calculate the normal from the peptide plane and store as the node rotation
                Vector3 vertexA = residue.AlphaCarbon.Position;
                Vector3 vertexB = residue.CarbonylCarbon.Position;
                Vector3 vertexC = residue.CarbonylOxygen.Position;

                // flip coord system for Unity
                vertexA.z *= -1;
                vertexB.z *= -1;
                vertexC.z *= -1;

                //// create a triangle to show the peptide plane on the model for debugging purposes
                //GameObject residuePlane = createTriangle(vertexA, vertexB, vertexC);
                //residuePlane.name = "ResiduePlane";
                //AddMeshToModel(residuePlane, StructureParent);

                Vector3 direction = Vector3.Cross(vertexB - vertexA, vertexC - vertexA);
                Vector3 normal    = Vector3.Normalize(direction);

                if (structureInformation != null && structureInformation.type == SecondaryStructureType.BetaSheet || lastType == SecondaryStructureType.BetaSheet)
                {
                    if (Vector3.Dot(normal, lastNormal) < 0)
                    {
                        normal *= -1;
                    }

                    if (lastType != SecondaryStructureType.BetaSheet)
                    {
                        averagedNormal = normal;
                    }
                    else
                    {
                        averagedNormal += normal;
                        averagedNormal.Normalize();
                        normal = averagedNormal;
                    }
                }

                node.Rotation = normal;

                lastNormal = normal;
                if (structureInformation != null)
                {
                    lastType = structureInformation.type;
                }
                else
                {
                    lastType = SecondaryStructureType.Coil;
                }

                nodes.Add(node);
            }


            if (renderSettings.SmoothNodes)
            {
                nodes = smoothMeshNodes(nodes);
            }

            //// draw debug line from node points along rotation vector
            //for (int q = 0; q < nodePositions.Count; q++) {

            //    Vector3 fromPosition = nodePositions[q];
            //    Vector3 toPosition = fromPosition + nodeRotations[q] * 0.3f;
            //    GameObject line = createLine(fromPosition, toPosition, Color.white, Color.red);
            //    AddMeshToModel(line, StructureParent);
            //}

            List <Vector3> nodePositions = new List <Vector3>();

            foreach (DynamicMeshNode node in nodes)
            {
                nodePositions.Add(node.Position);
            }
            List <DynamicMeshNode> splineNodes  = new List <DynamicMeshNode>();
            IEnumerable            splinePoints = Interpolate.NewCatmullRom(nodePositions.ToArray(), interpolation, false);
            int j = 0;

            foreach (Vector3 position in splinePoints)
            {
                int nodeIndex = j / (interpolation + 1);
                int splinePointsSinceLastNode = j % (interpolation + 1);

                DynamicMeshNode node = new DynamicMeshNode();
                node.Position = position;

                //int colorIndex = nodeIndex % DebugColors.Count;
                //node.VertexColor = DebugColors[colorIndex];

                node.VertexColor = nodes[nodeIndex].VertexColor;
                node.Type        = nodes[nodeIndex].Type;

                // set the mesh rotations for the node
                // dont do rotations on tube structures
                switch (node.Type)
                {
                case DynamicMeshNodeType.Ribbon:
                case DynamicMeshNodeType.RibbonHead:
                case DynamicMeshNodeType.SpiralRibbon:

                    if (nodeIndex < nodes.Count - 1)
                    {
                        float percentThroughNode = (float)splinePointsSinceLastNode / ((float)interpolation + 1f);
                        node.Rotation = Vector3.Lerp((Vector3)nodes[nodeIndex].Rotation, (Vector3)nodes[nodeIndex + 1].Rotation, percentThroughNode);
                    }
                    else       // last node
                    {
                        node.Rotation = (Vector3)nodes[nodeIndex].Rotation;
                    }

                    break;
                }

                splineNodes.Add(node);
                j++;
            }

            DynamicMesh dynamicMesh = new DynamicMesh(splineNodes, radius, resolution, interpolation + 1);

            structureMesh = dynamicMesh.Build(Settings.DebugFlag);

            return(structureMesh);
        }
        // Parses sheet secondary structure object specified by the pdb-file line
        void ParseSheet(string pdbLine)
        {
            Chain chain;
            char chainId = pdbLine[21];
            if (!chains.TryGetValue(chainId, out chain)) chains[chainId] = chain = new Chain() { Name = chainId.ToString() };

            SecondaryStructure sheet = new SecondaryStructure();
            sheet.StructureType = SecondaryStructureType.Sheet;
            sheet.FirstResidueSequenceNumber = int.Parse(pdbLine.Substring(22, 4), CultureInfo.InvariantCulture);
            sheet.LastResidueSequenceNumber = int.Parse(pdbLine.Substring(33, 4), CultureInfo.InvariantCulture);

            chain.SecondaryStructures.Add(sheet);
        }
        // Parses helix secondary structure object specified by the pdb-file line
        void ParseHelix(string pdbLine)
        {
            Chain chain;
            char chainId = pdbLine[19];
            if (!chains.TryGetValue(chainId, out chain)) chains[chainId] = chain = new Chain() { Name = chainId.ToString() };

            SecondaryStructure helix = new SecondaryStructure();
            helix.StructureType = SecondaryStructureType.Helix;
            helix.FirstResidueSequenceNumber = int.Parse(pdbLine.Substring(21, 4), CultureInfo.InvariantCulture);
            helix.LastResidueSequenceNumber = int.Parse(pdbLine.Substring(33, 4), CultureInfo.InvariantCulture);

            chain.SecondaryStructures.Add(helix);
        }