/// <summary> /// Completes the layout of a partially laid out ring. /// </summary> /// <param name="rset">ring set</param> /// <param name="ring">the ring to complete</param> /// <param name="bondLength">the bond length</param> internal bool CompletePartiallyPlacedRing(IRingSet rset, IRing ring, double bondLength) { if (ring.IsPlaced) { return(true); } IRing partiallyPlacedRing = Molecule.Builder.NewRing(); foreach (IAtom atom in ring.Atoms) { if (atom.Point2D != null) { atom.IsPlaced = true; } } AtomPlacer.CopyPlaced(partiallyPlacedRing, ring); if (partiallyPlacedRing.Atoms.Count > 1 && partiallyPlacedRing.Atoms.Count < ring.Atoms.Count) { PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Fused, bondLength); PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Bridged, bondLength); PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Spiro, bondLength); ring.IsPlaced = true; return(true); } else { return(false); } }
/// <summary> /// Place ring with user provided angles. /// </summary> /// <param name="ring">the ring to place.</param> /// <param name="ringCenter">center coordinates of the ring.</param> /// <param name="bondLength">given bond length.</param> /// <param name="startAngles">a map with start angles when drawing the ring.</param> public virtual void PlaceRing(IRing ring, Vector2 ringCenter, double bondLength, IReadOnlyDictionary <int, double> startAngles) { var radius = GetNativeRingRadius(ring, bondLength); var addAngle = 2 * Math.PI / ring.RingSize; var startAtom = ring.Atoms[0]; Vector2 p = new Vector2((ringCenter.X + radius), ringCenter.Y); startAtom.Point2D = p; var startAngle = Math.PI * 0.5; // Different ring sizes get different start angles to have visually // correct placement int ringSize = ring.RingSize; startAngle = startAngles[ringSize]; var bonds = ring.GetConnectedBonds(startAtom); // Store all atoms to draw in consecutive order relative to the chosen bond. var atomsToDraw = new List <IAtom>(); IAtom currentAtom = startAtom; IBond currentBond = (IBond)bonds.First(); for (int i = 0; i < ring.Bonds.Count; i++) { currentBond = ring.GetNextBond(currentBond, currentAtom); currentAtom = currentBond.GetOther(currentAtom); atomsToDraw.Add(currentAtom); } AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius); }
public void TriangleTest() { List <IAtom> atoms = new List <IAtom> { new Atom("C"), new Atom("C"), new Atom("C") }; AtomPlacer placer = new AtomPlacer(); AtomPlacer.PopulatePolygonCorners(atoms, Vector2.Zero, 0, 10, 10); foreach (var atom in atoms) { Assert.IsNotNull(atom.Point2D); } }
public void EmptyAtomsListTest() { List <IAtom> atoms = new List <IAtom>(); // switch on debugging, to see if NPE is thrown AtomPlacer placer = new AtomPlacer(); bool npeThrown = false; try { AtomPlacer.PopulatePolygonCorners(atoms, Vector2.Zero, 0, 10, 10); } catch (NullReferenceException) { npeThrown = true; } Assert.IsFalse(npeThrown, "Null pointer for emp.Y atoms list"); }
/// <summary> /// Positions the aliphatic substituents of a ring system /// </summary> /// <param name="rs">The RingSystem for which the substituents are to be laid out</param> /// <returns>A list of atoms that where laid out</returns> public IAtomContainer PlaceRingSubstituents(IRingSet rs, double bondLength) { Debug.WriteLine("RingPlacer.PlaceRingSubstituents() start"); IRing ring = null; IAtom atom = null; IAtomContainer unplacedPartners = rs.Builder.NewAtomContainer(); IAtomContainer sharedAtoms = rs.Builder.NewAtomContainer(); IAtomContainer primaryAtoms = rs.Builder.NewAtomContainer(); IAtomContainer treatedAtoms = rs.Builder.NewAtomContainer(); for (int j = 0; j < rs.Count; j++) { ring = (IRing)rs[j]; // Get the j-th Ring in RingSet rs for (int k = 0; k < ring.Atoms.Count; k++) { unplacedPartners.RemoveAllElements(); sharedAtoms.RemoveAllElements(); primaryAtoms.RemoveAllElements(); atom = ring.Atoms[k]; var rings = rs.GetRings(atom); var centerOfRingGravity = GeometryUtil.Get2DCenter(rings); AtomPlacer.PartitionPartners(atom, unplacedPartners, sharedAtoms); AtomPlacer.MarkNotPlaced(unplacedPartners); try { for (int f = 0; f < unplacedPartners.Atoms.Count; f++) { Debug.WriteLine("placeRingSubstituents->unplacedPartners: " + (Molecule.Atoms.IndexOf(unplacedPartners.Atoms[f]) + 1)); } } catch (Exception) { } treatedAtoms.Add(unplacedPartners); if (unplacedPartners.Atoms.Count > 0) { AtomPlacer.DistributePartners(atom, sharedAtoms, centerOfRingGravity, unplacedPartners, bondLength); } } } Debug.WriteLine("RingPlacer.PlaceRingSubstituents() end"); return(treatedAtoms); }
public void Cumulated_x3() { var m = new AtomContainer(); m.Atoms.Add(Atom("C", 3)); m.Atoms.Add(Atom("C", 1)); m.Atoms.Add(Atom("C", 0)); m.Atoms.Add(Atom("C", 0)); m.Atoms.Add(Atom("C", 1)); m.Atoms.Add(Atom("C", 3)); m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double); m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double); m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Double); m.AddBond(m.Atoms[4], m.Atoms[5], BondOrder.Single); m.Atoms[0].Point2D = Vector2.Zero; m.Atoms[0].IsPlaced = true; AtomPlacer atomPlacer = new AtomPlacer { Molecule = m }; var v = new Vector2(0, 1.5); atomPlacer.PlaceLinearChain(m, ref v, 1.5); Vector2 p1 = m.Atoms[1].Point2D.Value; Vector2 p2 = m.Atoms[2].Point2D.Value; Vector2 p3 = m.Atoms[3].Point2D.Value; Vector2 p4 = m.Atoms[4].Point2D.Value; Vector2 p2p1 = new Vector2(p1.X - p2.X, p1.Y - p2.Y); Vector2 p2p3 = new Vector2(p3.X - p2.X, p3.Y - p2.Y); Vector2 p3p2 = new Vector2(p2.X - p3.X, p2.Y - p3.Y); Vector2 p3p4 = new Vector2(p4.X - p3.X, p4.Y - p3.Y); p2p1 = Vector2.Normalize(p2p1); p2p3 = Vector2.Normalize(p2p3); p3p2 = Vector2.Normalize(p3p2); p3p4 = Vector2.Normalize(p3p4); //Assert.AreEqual(Math.PI, Math.Acos(p2p1.X * p2p3.X + p2p1.Y * p2p3.Y), 0.05); //Assert.AreEqual(Math.PI, Math.Acos(p3p2.X * p3p4.X + p3p2.Y * p3p4.Y), 0.05); Assert.AreEqual(-1, p2p1.X * p2p3.X + p2p1.Y * p2p3.Y, 0.001); Assert.AreEqual(-1, p3p2.X * p3p4.X + p3p2.Y * p3p4.Y, 0.001); }
public void Cumulated_x2() { var m = new AtomContainer(); m.Atoms.Add(Atom("C", 3)); m.Atoms.Add(Atom("C", 1)); m.Atoms.Add(Atom("C", 0)); m.Atoms.Add(Atom("C", 1)); m.Atoms.Add(Atom("C", 3)); m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double); m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double); m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Single); m.Atoms[0].Point2D = Vector2.Zero; m.Atoms[0].IsPlaced = true; AtomPlacer atomPlacer = new AtomPlacer { Molecule = m }; var v = new Vector2(0, 1.5); atomPlacer.PlaceLinearChain(m, ref v, 1.5); Vector2 p1 = m.Atoms[1].Point2D.Value; Vector2 p2 = m.Atoms[2].Point2D.Value; Vector2 p3 = m.Atoms[3].Point2D.Value; Vector2 p2p1 = new Vector2(p1.X - p2.X, p1.Y - p2.Y); Vector2 p2p3 = new Vector2(p3.X - p2.X, p3.Y - p2.Y); p2p1 = Vector2.Normalize(p2p1); p2p3 = Vector2.Normalize(p2p3); double round = (float)(p2p1.X * p2p3.X + p2p1.Y * p2p3.Y); // rounding the value to avoid over 1 value. double theta = Math.Acos(round); Assert.AreEqual(Math.PI, theta, 0.05); }
/// <summary> /// Generated coordinates for a given ring, which is fused to another ring. /// The rings share exactly one bond. /// </summary> /// <param name="ring">The ring to be placed</param> /// <param name="sharedAtoms">The atoms of this ring, also members of another ring, which are already placed</param> /// <param name="ringCenterVector">A vector pointing the the center of the new ring</param> /// <param name="bondLength">The standard bondlength</param> public virtual void PlaceFusedRing(IRing ring, IAtomContainer sharedAtoms, Vector2 ringCenterVector, double bondLength) { Debug.WriteLine("RingPlacer.PlaceFusedRing() start"); IAtom beg = sharedAtoms.Atoms[0]; IAtom end = sharedAtoms.Atoms[1]; Vector2 pBeg = beg.Point2D.Value; Vector2 pEnd = end.Point2D.Value; // fuse the ring perpendicular to the bond, ring center is not // sub-optimal if non-regular/convex polygon (e.g. macro cycle) ringCenterVector = GetPerpendicular(pBeg, pEnd, ringCenterVector); double radius = GetNativeRingRadius(ring, bondLength); double newRingPerpendicular = Math.Sqrt(Math.Pow(radius, 2) - Math.Pow(bondLength / 2, 2)); ringCenterVector = Vector2.Normalize(ringCenterVector); Debug.WriteLine($"placeFusedRing->: ringCenterVector.Length {ringCenterVector.Length()}"); ringCenterVector *= newRingPerpendicular; Vector2 ringCenter = GetMidPoint(pBeg, pEnd); ringCenter += ringCenterVector; Vector2 originRingCenterVector = ringCenter; pBeg -= originRingCenterVector; pEnd -= originRingCenterVector; double occupiedAngle = Angle(pBeg, pEnd); double remainingAngle = (2 * Math.PI) - occupiedAngle; double addAngle = remainingAngle / (ring.RingSize - 1); Debug.WriteLine("placeFusedRing->occupiedAngle: " + Vectors.RadianToDegree(occupiedAngle)); Debug.WriteLine("placeFusedRing->remainingAngle: " + Vectors.RadianToDegree(remainingAngle)); Debug.WriteLine("placeFusedRing->addAngle: " + Vectors.RadianToDegree(addAngle)); IAtom startAtom; double centerX = ringCenter.X; double centerY = ringCenter.Y; double xDiff = beg.Point2D.Value.X - end.Point2D.Value.X; double yDiff = beg.Point2D.Value.Y - end.Point2D.Value.Y; double startAngle;; int direction = 1; // if bond is vertical if (xDiff == 0) { Debug.WriteLine("placeFusedRing->Bond is vertical"); //starts with the lower Atom if (beg.Point2D.Value.Y > end.Point2D.Value.Y) { startAtom = beg; } else { startAtom = end; } //changes the drawing direction if (centerX < beg.Point2D.Value.X) { direction = 1; } else { direction = -1; } } // if bond is not vertical else { //starts with the left Atom if (beg.Point2D.Value.X > end.Point2D.Value.X) { startAtom = beg; } else { startAtom = end; } //changes the drawing direction if (centerY - beg.Point2D.Value.Y > (centerX - beg.Point2D.Value.X) * yDiff / xDiff) { direction = 1; } else { direction = -1; } } startAngle = GeometryUtil.GetAngle(startAtom.Point2D.Value.X - ringCenter.X, startAtom.Point2D.Value.Y - ringCenter.Y); IAtom currentAtom = startAtom; // determine first bond in Ring // int k = 0; // for (k = 0; k < ring.GetElectronContainerCount(); k++) { // if (ring.GetElectronContainer(k) is IBond) break; // } IBond currentBond = sharedAtoms.Bonds[0]; var atomsToDraw = new List <IAtom>(); for (int i = 0; i < ring.Bonds.Count - 2; i++) { currentBond = ring.GetNextBond(currentBond, currentAtom); currentAtom = currentBond.GetOther(currentAtom); atomsToDraw.Add(currentAtom); } addAngle = addAngle * direction; #if DEBUG Debug.WriteLine("placeFusedRing->startAngle: " + Vectors.RadianToDegree(startAngle)); Debug.WriteLine("placeFusedRing->addAngle: " + Vectors.RadianToDegree(addAngle)); Debug.WriteLine("placeFusedRing->startAtom is: " + (Molecule.Atoms.IndexOf(startAtom) + 1)); Debug.WriteLine("AtomsToDraw: " + AtomPlacer.ListNumbers(Molecule, atomsToDraw)); #endif AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius); }
/// <summary> /// Generated coordinates for a given ring, which is connected to a spiro ring. /// The rings share exactly one atom. /// </summary> /// <param name="ring">The ring to be placed</param> /// <param name="sharedAtoms">The atoms of this ring, also members of another ring, which are already placed</param> /// <param name="sharedAtomsCenter">The geometric center of these atoms</param> /// <param name="ringCenterVector">A vector pointing the center of the new ring</param> /// <param name="bondLength">The standard bond length</param> public virtual void PlaceSpiroRing(IRing ring, IAtomContainer sharedAtoms, Vector2 sharedAtomsCenter, Vector2 ringCenterVector, double bondLength) { var startAtom = sharedAtoms.Atoms[0]; var mBonds = Molecule.GetConnectedBonds(sharedAtoms.Atoms[0]).ToReadOnlyList(); var degree = mBonds.Count; Debug.WriteLine($"placeSpiroRing: D={degree}"); // recalculate the ringCentreVector if (degree != 4) { int numPlaced = 0; foreach (var bond in mBonds) { var nbr = bond.GetOther(sharedAtoms.Atoms[0]); if (!nbr.IsPlaced) { continue; } numPlaced++; } if (numPlaced == 2) { // nudge the shared atom such that bond lengths will be // equal startAtom.Point2D += ringCenterVector; sharedAtomsCenter += ringCenterVector; } var theta = Math.PI - (2 * Math.PI / (degree / 2)); Rotate(ringCenterVector, theta); } var radius = GetNativeRingRadius(ring, bondLength); var ringCenter = sharedAtomsCenter; if (degree == 4) { ringCenterVector = Vector2.Normalize(ringCenterVector); ringCenterVector *= radius; } else { // spread things out a little for multiple spiro centres ringCenterVector = Vector2.Normalize(ringCenterVector); ringCenterVector *= (2 * radius); } ringCenter += ringCenterVector; var addAngle = 2 * Math.PI / ring.RingSize; var currentAtom = startAtom; var startAngle = GeometryUtil.GetAngle( startAtom.Point2D.Value.X - ringCenter.X, startAtom.Point2D.Value.Y - ringCenter.Y); // Get one bond connected to the spiro bridge atom. It doesn't matter in which direction we draw. var rBonds = ring.GetConnectedBonds(startAtom); var currentBond = rBonds.First(); var atomsToDraw = new List <IAtom>(); // Store all atoms to draw in consequtive order relative to the chosen bond. for (int i = 0; i < ring.Bonds.Count; i++) { currentBond = ring.GetNextBond(currentBond, currentAtom); currentAtom = currentBond.GetOther(currentAtom); if (!currentAtom.Equals(startAtom)) { atomsToDraw.Add(currentAtom); } } Debug.WriteLine($"currentAtom {currentAtom}"); Debug.WriteLine($"startAtom {startAtom}"); AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius); }
private void PlaceBridgedRing(IRing ring, IAtomContainer sharedAtoms, Vector2 sharedAtomsCenter, Vector2 ringCenterVector, double bondLength) { IAtom[] bridgeAtoms = GetBridgeAtoms(sharedAtoms); IAtom bondAtom1 = bridgeAtoms[0]; IAtom bondAtom2 = bridgeAtoms[1]; List <IAtom> otherAtoms = new List <IAtom>(); foreach (IAtom atom in sharedAtoms.Atoms) { if (atom != bondAtom1 && atom != bondAtom2) { otherAtoms.Add(atom); } } bool snap = ring.GetProperty <bool>(SnapHint, false); bool swap = snap ? Det(bondAtom1.Point2D.Value, GeometryUtil.Get2DCenter(otherAtoms), bondAtom2.Point2D.Value) < 0 : Det(bondAtom1.Point2D.Value, GeometryUtil.Get2DCenter(otherAtoms), bondAtom2.Point2D.Value) > 0; if (swap) { IAtom tmp = bondAtom1; bondAtom1 = bondAtom2; bondAtom2 = tmp; } Vector2 bondAtom1Vector = bondAtom1.Point2D.Value; Vector2 bondAtom2Vector = bondAtom2.Point2D.Value; Vector2 midPoint = GetMidPoint(bondAtom1Vector, bondAtom2Vector); Vector2 ringCenter; double radius = GetNativeRingRadius(ring, bondLength); double offset = 0; if (snap) { ringCenter = midPoint; ringCenterVector = GetPerpendicular(bondAtom1Vector, bondAtom2Vector, new Vector2(midPoint.X - sharedAtomsCenter.X, midPoint.Y - sharedAtomsCenter.Y)); offset = 0; foreach (IAtom atom in otherAtoms) { double dist = Vector2.Distance(atom.Point2D.Value, midPoint); if (dist > offset) { offset = dist; } } } else { ringCenter = sharedAtomsCenter; } Vector2.Normalize(ringCenterVector); ringCenterVector = ringCenterVector * (radius - offset); ringCenter += ringCenterVector; bondAtom1Vector -= ringCenter; bondAtom2Vector -= ringCenter; int numUnplaced = ring.RingSize - sharedAtoms.Atoms.Count; double dot = bondAtom2Vector.X * bondAtom1Vector.X + bondAtom2Vector.Y * bondAtom1Vector.Y; double det = bondAtom2Vector.X * bondAtom1Vector.Y - bondAtom2Vector.Y * bondAtom1Vector.X; // theta remain/step double tRemain = Math.Atan2(det, dot); if (tRemain < 0) { tRemain = Math.PI + (Math.PI + tRemain); } double tStep = tRemain / (numUnplaced + 1); Debug.WriteLine($"placeBridgedRing->tRemain: {Vectors.RadianToDegree(tRemain)}"); Debug.WriteLine($"placeBridgedRing->tStep: {Vectors.RadianToDegree(tStep)}"); double startAngle; startAngle = GeometryUtil.GetAngle(bondAtom1.Point2D.Value.X - ringCenter.X, bondAtom1.Point2D.Value.Y - ringCenter.Y); IAtom currentAtom = bondAtom1; IBond currentBond = sharedAtoms.GetConnectedBonds(currentAtom).First(); List <IAtom> atoms = new List <IAtom>(); for (int i = 0; i < ring.Bonds.Count; i++) { currentBond = ring.GetNextBond(currentBond, currentAtom); currentAtom = currentBond.GetOther(currentAtom); if (!sharedAtoms.Contains(currentAtom)) { atoms.Add(currentAtom); } } #if DEBUG Debug.WriteLine($"{nameof(PlaceBridgedRing)}->atomsToPlace: {AtomPlacer.ListNumbers(Molecule, atoms)}"); Debug.WriteLine($"{nameof(PlaceBridgedRing)}->startAngle: {Vectors.RadianToDegree(startAngle)}"); Debug.WriteLine($"{nameof(PlaceBridgedRing)}->tStep: {Vectors.RadianToDegree(tStep)}"); #endif AtomPlacer.PopulatePolygonCorners(atoms, ringCenter, startAngle, -tStep, radius); }