import java.awt.Component; import java.awt.Dimension; import java.awt.Rectangle; import java.util.Random; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.vecmath.Point2f; import javax.vecmath.Point3f; import org.cellmicrocosmos.cm2.membranepackingalgorithm.*; /** * This is a very simple membrane algorithm that i wrote to show a good example * how a membrane algorithm works. It simply places new lipids at a rondom * position until the desired number of lipids has been reached or it is * getting aborted. It provides a small GUI so that you can see how to use this * feature and it also supports microdomains * * * @author Tim Dingersen dackelnose@gmx.net * */ public class MyMembraneAlgorithm extends MembranePackingAlgorithm { // the desired number of lipids int desiredNumberOfLipids; // indicates if lipids should be rotated boolean rotateLipids; // a flag that can be toggled to abort the algorithm boolean stop = false; /* * This method gets invoked before the fillWithLipids() process is started * This is the point where you should present your GUI, like i'm doing here */ public boolean buildAndShowGui() { /* * Testing if there are any lipid templates available for the default * area or at least in any microdomain. */ boolean makesSense = this.hasMembraneAnyExtracellularLipids() || this.hasMembraneAnyIntracellularLipids(); for (int i = 0; i < this.getMicroDomainCount(); i++) { if ((this.getMicroDomainAt(i).hasAnyExtracellularLipids() || this .getMicroDomainAt(i).hasAnyIntracellularLipids()) == false) makesSense = false; } if (!makesSense) { JOptionPane.showMessageDialog(null, "No lipids that could be added available!", "No lipids", JOptionPane.ERROR_MESSAGE); return false; } // building up the options panel for the GUI JPanel controlPanel = new JPanel(); JLabel textLabel = new JLabel("Desired number of lipids: "); JTextField textField = new JTextField(); JCheckBox checkBox = new JCheckBox("Rotate lipids"); textField.setPreferredSize(new Dimension(100, 30)); controlPanel.add(textLabel); controlPanel.add(textField); controlPanel.add(checkBox); /* * showing the GUI to the user. The returnvalue indicates if the user * confirmed or cancelled the dialog so far */ boolean returnValue = MembranePackingAlgorithmGUI.popUpGUI(controlPanel, "Options", "Set up some options here"); // returning false if all should be cancelled if (returnValue == false) return false; /* * trying to retrieve the number of desired lipids from the textfield. * in case of an error the algorithm gets cancelled with a message */ try { this.desiredNumberOfLipids = Integer.parseInt(textField.getText()); if (this.desiredNumberOfLipids < 1 || this.desiredNumberOfLipids > 1000) throw new Exception(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "You need to enter a number between 1 and 1000\n" + "into the textfield!", "Invalid Value", JOptionPane.ERROR_MESSAGE); return false; } this.rotateLipids = checkBox.isSelected(); /* * the user settings are ok plus the user must have confirmed if the * thread reaches this code, so true gets returned in order to proceed */ return true; } /* * This method gets called when the algorithm gets started. Remember: The * algorithm finishes when the thread leaves this method */ public void fillWithLipids() throws Exception { // the current count of lipids that have been added on this run. int currentCount = 0; // the value indicates which membraneside should be handled int mSide = MembranePackingAlgorithm.INTRA; /* * starting a loop that runs until the terminal condition is reached or * the stop flag has been set on true */ while (stop == false && currentCount < this.desiredNumberOfLipids) { // toggling membranesides to handle at each loop if (mSide == MembranePackingAlgorithm.INTRA) mSide = MembranePackingAlgorithm.EXTRA; else mSide = MembranePackingAlgorithm.INTRA; // creating a random position on the x/z plane Point2f randomPosition = this.createRandomXZPosition(new Rectangle( 0, 0, this.getMembraneSize().width, this.getMembraneSize().height)); // initializing the new lipid for this run Lipid newLipid; /* * determining the domainID for the microdomain at the random point. * If no microdomains are available or if the point hits the default * area, the value 0 gets returned */ int domainID = this.getMicroDomainAtPoint(randomPosition); /* * creating the new lipid by using the convenient methods for this * if the random point should hit a microdomain, then the methods of * the specified microdomains are used in order to get the proper * lipids */ if (domainID > 0) { // the lowest domainID is 1, so we have to use domainID-1 as the // index MicroDomain domain = this.getMicroDomainAt(domainID - 1); newLipid = mSide == EXTRA ? domain .createNextExtracellularLipid() : domain .createNextIntracellularLipid(); } else { newLipid = mSide == EXTRA ? this.createNextExtracellularLipid() : this.createNextIntracellularLipid(); } /* * if the lipid should be null at this point, then the user dind't * add any lipid templates to the membrane side of the domain (or * default area) we are trying to create it for. */ if (newLipid == null) continue; /* * Assigning the new position to the lipid while maintaining its * y-translation that we don't want to change in this algorithm */ newLipid.setPosition(new Point3f(randomPosition.x, newLipid .getYTranslation(), randomPosition.y)); // randomly rotating the lipid if this option has been selected if (this.rotateLipids) newLipid.rotY(new Random().nextInt(359) + new Random().nextFloat()); /* * The most interesting part: Testing for intersections. First for * intersections with the borders, then for intersections with any * other molecules. If an intersection occured then we could * initiate corrections, but instead we simply retry from the top */ if (this.intersectsBorders(newLipid) || this.intersects(newLipid)) continue; /* * If the lipid doesn't intersect at it's current alignment, we can * add itonto its specified membrane side. */ if (mSide == EXTRA) this.addExtracellularLipid(newLipid); else this.addIntracellularLipid(newLipid); // one more lipid added here :) currentCount++; } } public String getFullInformation() { // this string is getting displayed in the textarea were the algorithms // are selected return "This algorithm simply places all lipids\n" + "at random position"; } public String getName() { // the chosen name for this algorithm. It has to return a unique name in // order to get accepted return "My Algorithm"; } public Component getRuntimeControlPanel() { // we don't provide any runtime controls for this simple algorithm return null; } public String getShortInformation() { // you can leave out some information if you want return null; } public String getStringRepresentationOfSetupParameters() { // returning a String that describes the users setup return "Desired Lipids: " + this.desiredNumberOfLipids + "\n" + "Rotate Lipids: " + this.rotateLipids; } public boolean overrideExistingLipids() { // existing lipids may remain in this case return false; } public void stopAlgorithm() { // setting the stop flag on true, forcing the algorithm to stop at the // next loop this.stop = true; } public boolean supportsMicrodomains() { // this algorithm provides a full microdomain support return true; } public boolean supportsAbsoluteValues() { // this algorithm does not support absolute values return false; } }