GraphComponent ComposeRendering(GraphComponent component, Strand strand, double y0 = 0) // We render a strand as a linear sequence of spring-separated particles. // We initially lay things out in a linear line, either to the right (for // normally oriented strands) or left (for reverse oriented strands). { if (null==component) component = new GraphComponent(this); // // Compose edges for domains along the strand backbone // List<GraphVertex> strandVertices = new List<GraphVertex>(); List<GraphEdge> strandEdges = new List<GraphEdge>(); // double count = strand.AllDomains().Count(); double length = strand.AllDomains().Sum(d => this.EdgeFromDomain(component, d).PreferredLength); double radius = length / 2.0 / Math.PI; // radius /= 3; // hack -> otherwise the forces pulling the circle together overshoot // int i = 0; foreach (Domain d in strand.AllDomains()) { GraphEdge edge = this.EdgeFromDomain(component, d); strandEdges.Add(edge); // // The first vertex in the strand needs to be explicitly positioned // double x,y; // if (strandVertices.IsEmpty()) { strandVertices.Add(edge.A); if (strand.IsCircular) x = strand.Orientation==Strand.Direction.FiveToThree ? -radius : radius; else x = 0; edge.A.SetLocation(x,y0); } // // Position the B vertex of the edge // if (strand.IsCircular) { double theta = i / count * 2 * Math.PI; x = radius * Math.Cos(theta); y = radius * Math.Sin(theta); if (strand.Orientation==Strand.Direction.FiveToThree) { x = -x; y = -y; } } else { y = y0; x = edge.PreferredLength; if (strand.Orientation == Strand.Direction.ThreeToFive) x = -x; x += strandVertices.Last().Location.X; } // edge.B.SetLocation(x, y); strandVertices.Add(edge.B); // i++; } // // Make a spline for the strand as a whole // if (strandVertices.NotEmpty()) { VisualStrand vStrand = new VisualStrand(strand, component, strandEdges, strandVertices); this.mpStrandVisualization[strand] = vStrand; // // Place ticks at the ends of each of the interior domains in the strand // for (i = strand.IsCircular ? 0 : 1; i < strandVertices.Count-1; i++) { new VisualDomainEndTick(component, vStrand, i, Constants.DomainEndLength); } // // Add domain labels and nucleotides // foreach (Domain d in strand.AllDomains()) { i = d.StrandIndex; // VisualStrandLabel label = new VisualStrandLabel(component, vStrand, i, i+1, d.FullDisplayName, Constants.StyleDomainLabel); label.LabelType = VisualStrandLabel.LABELTYPE.DOMAIN; vStrand.NoteDomainLabel(i, label); // if (d.Nucleotides.Length > 0 && true) { label = new VisualStrandLabel(component, vStrand, i, i+1, d.Nucleotides, Constants.StyleNucleotides); label.LabelType = VisualStrandLabel.LABELTYPE.NUCLEOTIDE; label.FontVerticallyCentered = true; label.AutoReverseText = true; } } } // if (strandVertices.NotEmpty()) { // Help a bit with initial placement: left align if we moved leftward // from zero due to strand direction. // if (!strand.IsCircular && strand.Orientation == Strand.Direction.ThreeToFive) { double dx = -strandVertices.Last().Location.X; foreach (GraphVertex v in strandVertices) { v.TranslateBy(dx, 0); } } } // // Put in stiffness along the strand // foreach (Domain d in strand.AllDomains()) { if (d.CircularNextTo5 != null) { GraphEdge curEdge = this.EdgeFromDomain(component, d); GraphEdge prevEdge = this.EdgeFromDomain(component, d.CircularNextTo5); // new StraighteningForce(component, prevEdge.A, curEdge.A, curEdge.B, Constants.StraighteningStrength); } } // return component; }
public void NoteDomainLabel(int iDomain, VisualStrandLabel label) { this.domainLabels[iDomain] = label; }