/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.mahout.ga.watchmaker.travellingsalesman; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.List; import javax.swing.JApplet; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.WindowConstants; import org.uncommons.swing.SwingBackgroundTask; import org.uncommons.watchmaker.framework.FitnessEvaluator; /** * Applet for comparing evolutionary and brute force approaches to the Travelling Salesman problem. * * The original code is from <b>the Watchmaker project</b> (https://watchmaker.dev.java.net/). <br> * This class has been modified to add a main function that runs the JApplet inside a JDialog. */ public final class TravellingSalesman extends JApplet { private final ItineraryPanel itineraryPanel; private final StrategyPanel strategyPanel; private final ExecutionPanel executionPanel; private final FitnessEvaluator<List<String>> evaluator; /** * Creates the applet and lays out its GUI. */ public TravellingSalesman() { DistanceLookup distances = new EuropeanDistanceLookup(); evaluator = new RouteEvaluator(distances); itineraryPanel = new ItineraryPanel(distances.getKnownCities()); strategyPanel = new StrategyPanel(distances); executionPanel = new ExecutionPanel(); add(itineraryPanel, BorderLayout.WEST); Container innerPanel = new JPanel(new BorderLayout()); innerPanel.add(strategyPanel, BorderLayout.NORTH); innerPanel.add(executionPanel, BorderLayout.CENTER); add(innerPanel, BorderLayout.CENTER); executionPanel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { Collection<String> cities = itineraryPanel.getSelectedCities(); if (cities.size() < 4) { JOptionPane.showMessageDialog(TravellingSalesman.this, "Itinerary must include at least 4 cities.", "Error", JOptionPane.ERROR_MESSAGE); } else { try { setEnabled(false); TravellingSalesmanStrategy strategy = strategyPanel.getStrategy(); new TSSwingBackgroundTask(strategy, cities, executionPanel, evaluator).execute(); } catch (IllegalArgumentException ex) { JOptionPane.showMessageDialog(TravellingSalesman.this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); setEnabled(true); } } } }); validate(); } /** * Toggles whether the controls are enabled for input or not. * * @param b * Enables the controls if this flag is true, disables them otherwise. */ @Override public void setEnabled(boolean b) { itineraryPanel.setEnabled(b); strategyPanel.setEnabled(b); executionPanel.setEnabled(b); super.setEnabled(b); } public static void main(String[] args) { JDialog dialog = new JDialog((Frame) null, "Travelling Salesman Frame", true); dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); dialog.getContentPane().add(new TravellingSalesman()); dialog.pack(); dialog.setLocationRelativeTo(null); dialog.setVisible(true); } private final class TSSwingBackgroundTask extends SwingBackgroundTask<List<String>> { private long elapsedTime; private final TravellingSalesmanStrategy strategy; private final Collection<String> cities; private final ExecutionPanel executionPanel; private final FitnessEvaluator<List<String>> evaluator; private TSSwingBackgroundTask(TravellingSalesmanStrategy strategy, Collection<String> cities, ExecutionPanel executionPanel, FitnessEvaluator<List<String>> evaluator) { this.strategy = strategy; this.cities = cities; this.executionPanel = executionPanel; this.evaluator = evaluator; } @Override protected List<String> performTask() { long startTime = System.currentTimeMillis(); List<String> result = strategy.calculateShortestRoute(cities, executionPanel); elapsedTime = System.currentTimeMillis() - startTime; return result; } @Override protected void postProcessing(List<String> result) { executionPanel.appendOutput(createResultString(strategy.getDescription(), result, evaluator.getFitness(result, null), elapsedTime)); setEnabled(true); } /** * Helper method for formatting a result as a string for display. */ private String createResultString(String strategyDescription, List<String> shortestRoute, double distance, long elapsedTime) { StringBuilder buffer = new StringBuilder(100); buffer.append('['); buffer.append(strategyDescription); buffer.append("]\n"); buffer.append("ROUTE: "); for (String s : shortestRoute) { buffer.append(s); buffer.append(" -> "); } buffer.append(shortestRoute.get(0)); buffer.append('\n'); buffer.append("TOTAL DISTANCE: "); buffer.append(String.valueOf(distance)); buffer.append("km\n"); buffer.append("(Search Time: "); double seconds = (double) elapsedTime / 1000; buffer.append(String.valueOf(seconds)); buffer.append(" seconds)\n\n"); return buffer.toString(); } } }