/// <summary> /// Stretch all bonds in the shortest path between a pair of atoms in an /// attempt to resolve the overlap. The stretch that produces the minimum /// congestion is stored in the provided stack and coordinates with the congestion /// score returned. /// </summary> /// <param name="pair">congested atom pair</param> /// <param name="stack">best result vertices</param> /// <param name="coords">best result coordinates</param> /// <param name="firstVisit">visit map to avoid repeating work</param> /// <returns>congestion score of best result</returns> private double Stretch(AtomPair pair, IntStack stack, Vector2[] coords, Dictionary <IBond, AtomPair> firstVisit) { stackBackup.Clear(); var score = congestion.Score(); var min = score; foreach (var bond in pair.bndAt) { // don't stretch ring bonds if (bond.IsInRing) { continue; } if (bfix.Contains(bond)) { continue; } // has this bond already been tested as part of another pair if (!firstVisit.TryGetValue(bond, out AtomPair first)) { firstVisit[bond] = first = pair; } if (first != pair) { continue; } var beg = bond.Begin; var end = bond.End; var begIdx = idxs[beg]; var endIdx = idxs[end]; var begPriority = beg.GetProperty <int>(AtomPlacer.Priority); var endPriority = end.GetProperty <int>(AtomPlacer.Priority); Arrays.Fill(visited, false); if (begPriority < endPriority) { stack.len = Visit(visited, stack.xs, endIdx, begIdx, 0); } else { stack.len = Visit(visited, stack.xs, begIdx, endIdx, 0); } BackupCoords(backup, stack); if (begPriority < endPriority) { Stretch(stack, end, beg, pair.attempt * StrechStep); } else { Stretch(stack, beg, end, pair.attempt * StrechStep); } congestion.Update(visited, stack.xs, stack.len); if (PercDiff(score, congestion.Score()) >= ImprovementPrecThreshold && congestion.Score() < min) { BackupCoords(coords, stack); min = congestion.Score(); stackBackup.CopyFrom(stack); } RestoreCoords(stack, backup); congestion.Update(visited, stack.xs, stack.len); congestion.score = score; } stack.CopyFrom(stackBackup); return(min); }
/// <summary> /// Bend all bonds in the shortest path between a pair of atoms in an attempt /// to resolve the overlap. The bend that produces the minimum congestion is /// stored in the provided stack and coords with the congestion score /// returned. /// </summary> /// <param name="pair">congested atom pair</param> /// <param name="stack">best result vertices</param> /// <param name="coords">best result coords</param> /// <param name="firstVisit">visit map to avoid repeating work</param> /// <returns>congestion score of best result</returns> private double Bend(AtomPair pair, IntStack stack, Vector2[] coords, Dictionary <IBond, AtomPair> firstVisit) { stackBackup.Clear(); Trace.Assert(stack.len == 0); double score = congestion.Score(); double min = score; // special case: if we have an even length path where the two // most central bonds are cyclic but the next two aren't we bend away // from each other if (pair.bndAt.Count > 4 && (pair.bndAtCode & 0x1F) == 0x6) { var bndA = pair.bndAt[2]; var bndB = pair.bndAt[3]; if (bfix.Contains(bndA) || bfix.Contains(bndB)) { return(int.MaxValue); } var pivotA = GetCommon(bndA, pair.bndAt[1]); var pivotB = GetCommon(bndB, pair.bndAt[0]); if (pivotA == null || pivotB == null) { return(int.MaxValue); } Arrays.Fill(visited, false); int split = Visit(visited, stack.xs, idxs[pivotA], idxs[bndA.GetOther(pivotA)], 0); stack.len = Visit(visited, stack.xs, idxs[pivotB], idxs[bndB.GetOther(pivotB)], split); // perform bend one way BackupCoords(backup, stack); Bend(stack.xs, 0, split, pivotA, BendStep); Bend(stack.xs, split, stack.len, pivotB, -BendStep); congestion.Update(stack.xs, stack.len); if (PercDiff(score, congestion.Score()) >= ImprovementPrecThreshold) { BackupCoords(coords, stack); stackBackup.CopyFrom(stack); min = congestion.Score(); } // now bend the other way RestoreCoords(stack, backup); Bend(stack.xs, 0, split, pivotA, -BendStep); Bend(stack.xs, split, stack.len, pivotB, BendStep); congestion.Update(stack.xs, stack.len); if (PercDiff(score, congestion.Score()) >= ImprovementPrecThreshold && congestion.Score() < min) { BackupCoords(coords, stack); stackBackup.CopyFrom(stack); min = congestion.Score(); } // restore original coordinates and reset score RestoreCoords(stack, backup); congestion.Update(stack.xs, stack.len); congestion.score = score; } // general case: try bending acyclic bonds in the shortest // path from inside out else { // try bending all bonds and accept the best one foreach (var bond in pair.bndAt) { if (bond.IsInRing) { continue; } if (bfix.Contains(bond)) { continue; } // has this bond already been tested as part of another pair if (!firstVisit.TryGetValue(bond, out AtomPair first)) { firstVisit[bond] = first = pair; } if (first != pair) { continue; } var beg = bond.Begin; var end = bond.End; var begPriority = beg.GetProperty <int>(AtomPlacer.Priority); var endPriority = end.GetProperty <int>(AtomPlacer.Priority); Arrays.Fill(visited, false); if (begPriority < endPriority) { stack.len = Visit(visited, stack.xs, idxs[beg], idxs[end], 0); } else { stack.len = Visit(visited, stack.xs, idxs[end], idxs[beg], 0); } BackupCoords(backup, stack); // bend one way if (begPriority < endPriority) { Bend(stack.xs, 0, stack.len, beg, pair.attempt * BendStep); } else { Bend(stack.xs, 0, stack.len, end, pair.attempt * BendStep); } congestion.Update(visited, stack.xs, stack.len); if (PercDiff(score, congestion.Score()) >= ImprovementPrecThreshold && congestion.Score() < min) { BackupCoords(coords, stack); stackBackup.CopyFrom(stack); min = congestion.Score(); } // bend other way if (begPriority < endPriority) { Bend(stack.xs, 0, stack.len, beg, pair.attempt * -BendStep); } else { Bend(stack.xs, 0, stack.len, end, pair.attempt * -BendStep); } congestion.Update(visited, stack.xs, stack.len); if (PercDiff(score, congestion.Score()) >= ImprovementPrecThreshold && congestion.Score() < min) { BackupCoords(coords, stack); stackBackup.CopyFrom(stack); min = congestion.Score(); } RestoreCoords(stack, backup); congestion.Update(visited, stack.xs, stack.len); congestion.score = score; } } stack.CopyFrom(stackBackup); return(min); }