package uk.org.squirm3.model.level.validators; import java.awt.Polygon; import java.awt.geom.Point2D; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import uk.org.squirm3.model.Atom; import uk.org.squirm3.model.level.AtomValidator; import uk.org.squirm3.model.level.LevelMessages; import uk.org.squirm3.model.type.def.BasicType; public class CellDivisionValidator implements AtomValidator { @Override public void setup(final Collection<? extends Atom> atoms) { // TODO Auto-generated method stub } @Override public String evaluate(final Collection<? extends Atom> atoms, final LevelMessages messages) { // evaluation of this level is quite tricky. we could be strict and // insist on neat membranes but that is // not really the intention. // pseudocode: non-embedded connected components, identification of the // copies, check for template copy final List<List<Atom>> components = new LinkedList<List<Atom>>(); // stores a list of LinkedList's, the components for (final Atom a : atoms) { // is this atom already in a connected component? boolean already_seen = false; for (int iComponent = 0; iComponent < components.size(); iComponent++) { if (components.get(iComponent).contains(a)) { already_seen = true; break; } } if (already_seen) { continue; } // create a new connected component starting from this atom final LinkedList<Atom> component = new LinkedList<Atom>(); a.getAllConnectedAtoms(component); if (component.size() > 6) { components.add(component); } } if (components.size() != 2) { // two large components return messages.getError(1); } // neither component should be inside the other { final Polygon poly[] = new Polygon[2]; // assemble the two polygons (doesn't matter if they're a bit messy // at places) for (int iComp = 0; iComp < 2; iComp++) { final int NP = components.get(iComp).size(); final int px[] = new int[NP], py[] = new int[NP]; for (int i = 0; i < NP; i++) { final Atom a = components.get(iComp).get(i); px[i] = (int) a.getPhysicalPoint().getPositionX(); py[i] = (int) a.getPhysicalPoint().getPositionY(); } poly[iComp] = new Polygon(px, py, NP); } // check for either polygon having a point inside the other // (given that bond-crossing is forbidden, we expect this to be a // complete test of separatedness) for (int iComp = 0; iComp < 2; iComp++) { final List<Atom> c = components.get(iComp); final int NP = c.size(); for (int i = 0; i < NP; i++) { final Atom a = c.get(i); // is this point inside the other polygon? if (poly[1 - iComp].contains(new Point2D.Float(a .getPhysicalPoint().getPositionX(), a .getPhysicalPoint().getPositionY()))) { return messages.getError(2); } } } } // let's enforce that the template is a sequence of 2-connected atoms // starting with 'e' // and ending with 'f', with each end connected to 3+ connected atoms, // and only types a-d in between final Atom heads[] = new Atom[2]; // will put the pointers to the two // 'e' ends // here int n_found = 0; final Iterator<? extends Atom> iterator = atoms.iterator(); while (iterator.hasNext() && n_found < 2) { final Atom a = iterator.next(); if (a.getType() == BasicType.E && a.getState() != 0 && a.getBonds().size() == 2) { heads[n_found++] = a; } } if (n_found < 2) { return messages.getError(3); } // each head should be in a separate component final List<Atom> c1 = components.get(0), c2 = components.get(1); if (c1.contains(heads[0]) && !c2.contains(heads[1]) || c2.contains(heads[0]) && !c1.contains(heads[1])) { return messages.getError(4); } // work down each template, adding the type of each 2-connected atom to // sequence[i] final String sequence[] = {new String(), new String()}; for (int iCell = 0; iCell < 2; iCell++) { final List<Atom> seen = new LinkedList<Atom>(); Atom current = heads[iCell]; seen.add(current); sequence[iCell] = "e"; // let's get things started if (current.getBonds().getFirst().getBonds().size() == 2) { current = current.getBonds().getFirst(); } else { current = current.getBonds().getLast(); } while (sequence[iCell].length() < 10) { // if the current atom has other than 2 bonds then we are done if (current.getBonds().size() != 2) { break; } // append the type letter (a-f) to the string sequence[iCell] += current.getType().getCharacterIdentifier(); // add the current atom to the list so that we will know we have // seen it before seen.add(current); // move onto the next bond (we know this atom has exactly two) if (seen.contains(current.getBonds().getFirst())) { current = current.getBonds().get(1); } else { current = current.getBonds().getFirst(); } } // System.out.println(sequence[iCell]); if (sequence[iCell].length() != 6 || sequence[iCell].charAt(0) != 'e' || sequence[iCell].charAt(5) != 'f') { // parameter : // "Incorrect template sequence detected: "+sequence[iCell]; return messages.getError(5); } } if (sequence[0].compareTo(sequence[1]) != 0) { return messages.getError(6); } return null; } }