in core/indigo-core/layout/src/molecule_layout_graph_refine.cpp [132:430]
void MoleculeLayoutGraph::_refineCoordinates(const BiconnectedDecomposer& bc_decomposer, const PtrArray<MoleculeLayoutGraph>& bc_components,
const Array<int>& bc_tree)
{
RefinementState beg_state(*this);
RefinementState best_state(*this);
RefinementState new_state(*this);
QS_DEF(Array<int>, branch);
int v1, v2;
int v1c, v2c;
int i, j, n;
v1c = v1 = vertexBegin();
v2c = v2 = vertexNext(v1);
// Calculate initial energy
beg_state.copyFromGraph();
beg_state.calcEnergy();
beg_state.calcDistance(v1, v2);
best_state.copy(beg_state);
new_state.copy(beg_state);
// Look through all vertex pairs which are closer than 0.6
bool improved = true;
QS_DEF(RedBlackSet<int>, edges);
QS_DEF(Array<int>, components1);
QS_DEF(Array<int>, components2);
EnumContext context;
context.edges = &edges;
context.graph = this;
context.maxIterationNumber = max_iterations;
int max_improvements = max_iterations * max_iterations;
int n_improvements = 0;
while (improved)
{
if (max_improvements > 0 && n_improvements > max_improvements)
break;
n_improvements++;
improved = false;
edges.clear();
int n_edges = 0;
int n_enumerations = 0;
bool to_break = false;
new_state.copy(beg_state);
for (v1 = vertexBegin(); v1 < vertexEnd() && !to_break; v1 = vertexNext(v1))
for (v2 = vertexNext(v1); v2 < vertexEnd(); v2 = vertexNext(v2))
{
new_state.calcDistance(v1, v2);
if (new_state.dist > 0.36f)
continue;
// Check if they are from the same component
bool next_pair = false;
bc_decomposer.getVertexComponents(v1, components1);
bc_decomposer.getVertexComponents(v2, components2);
for (i = 0; i < components1.size() && !next_pair; i++)
for (j = 0; j < components2.size(); j++)
{
int comp1 = components1[i];
int comp2 = components2[j];
if (comp1 == comp2 && !bc_components[comp1]->isSingleEdge() && !bc_components[comp2]->isSingleEdge())
{
next_pair = true;
break;
}
}
if (next_pair)
continue;
// check iterations limit
if (max_iterations > 0 && n_enumerations > max_iterations)
{
to_break = true;
break;
}
n_enumerations++;
// Find acyclic edges on the all paths between v1 and v2
PathEnumerator path_enum(*this, v1, v2);
path_enum.cb_check_edge = _edge_check;
path_enum.cb_handle_path = _path_handle;
path_enum.context = &context;
context.iterationNumber = 0;
try
{
path_enum.process();
}
catch (Error)
{
// iterations limit reached
}
if (edges.size() == n_edges)
continue;
n_edges = edges.size();
if (beg_state.dist - 0.00001 > new_state.dist)
{
beg_state.dist = new_state.dist;
v1c = v1;
v2c = v2;
}
}
if (edges.size() == 0)
{
beg_state.applyToGraph();
break;
}
// Flipping
// Look through found edges
for (i = edges.begin(); i < edges.end(); i = edges.next(i))
{
if (max_improvements > 0 && n_improvements > max_improvements)
break;
n_improvements++;
// Try to flip branch
const Edge& edge = getEdge(edges.key(i));
if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0)
continue;
if (getVertex(edge.beg).degree() == 1 || getVertex(edge.end).degree() == 1)
continue;
Filter filter;
_makeBranches(branch, edges.key(i), filter);
new_state.flipBranch(filter, beg_state, edge.beg, edge.end);
new_state.calcEnergy();
if (new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
}
if (improved)
{ // finished becouse of flipped
beg_state.copy(best_state);
continue;
}
// Rotations
// Look through found edges
for (i = edges.begin(); i < edges.end(); i = edges.next(i))
{
if (max_improvements > 0 && n_improvements > max_improvements)
break;
n_improvements += 3;
// Try to rotate one branch by 10 degrees in both directions around both vertices
const Edge& edge = getEdge(edges.key(i));
if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0)
continue;
Filter filter;
_makeBranches(branch, edges.key(i), filter);
bool around_beg = _allowRotateAroundVertex(edge.beg);
bool around_end = _allowRotateAroundVertex(edge.end);
if (around_beg)
{
new_state.rotateBranch(filter, beg_state, edge.beg, 10);
new_state.calcDistance(v1c, v2c);
new_state.calcEnergy();
if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
new_state.rotateBranch(filter, beg_state, edge.beg, -10);
new_state.calcDistance(v1c, v2c);
new_state.calcEnergy();
if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
}
if (around_end)
{
new_state.rotateBranch(filter, beg_state, edge.end, 10);
new_state.calcDistance(v1c, v2c);
new_state.calcEnergy();
if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
new_state.rotateBranch(filter, beg_state, edge.end, -10);
new_state.calcDistance(v1c, v2c);
new_state.calcEnergy();
if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
}
// Stretching
// Try to stretch each edge with 1.6 ratio
if ((n = filter.count(*this)) != 1 && n != vertexCount() - 1)
{
new_state.stretchBranch(filter, beg_state, edge.beg, edge.end, 6);
new_state.calcDistance(v1c, v2c);
new_state.calcEnergy();
if (new_state.dist > beg_state.dist && new_state.energy + 20 < beg_state.energy && new_state.energy < best_state.energy - 0.00001)
{
improved = true;
best_state.copy(new_state);
}
}
}
if (improved)
beg_state.copy(best_state);
}
if (_n_fixed == 0)
{
if (!beg_state.is_small_cycle())
{
int center = -1;
long max_code = 0;
for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i))
if (getLayoutVertex(i).morgan_code > max_code)
{
center = i;
max_code = getLayoutVertex(i).morgan_code;
}
beg_state.calcHeight();
if (layout_orientation != UNCPECIFIED)
{
new_state.rotateLayout(beg_state, center, _2FLOAT(beg_state.calc_best_angle() / M_PI * 180.));
beg_state.copy(new_state);
if (layout_orientation == VERTICAL)
{
new_state.rotateLayout(beg_state, center, 90);
beg_state.copy(new_state);
}
}
else
{
for (float angle = -90.f; angle < 90.f + EPSILON; angle += 30.f)
{
new_state.rotateLayout(beg_state, center, angle);
new_state.calcHeight();
if (new_state.height < beg_state.height - EPSILON)
beg_state.copy(new_state);
}
}
}
}
beg_state.applyToGraph();
_excludeDandlingIntersections();
}