/// <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); }
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); }