001    package aima.gui.applications.search.map;
002    
003    import java.awt.Color;
004    import java.util.ArrayList;
005    import java.util.List;
006    
007    import aima.gui.framework.AgentAppFrame;
008    import aima.gui.framework.AgentView;
009    import aima.search.framework.SearchFactory;
010    import aima.search.map.Map;
011    import aima.search.map.Point2D;
012    
013    
014    /**
015     * Universal frame for experiments with route planning
016     * agents. It configures the agent application frame with some selectors
017     * and an agent view which is designed for cooperation with an
018     * {@link MapAgentModel}. Since items for scenario, agent, destination,
019     * agent, and heuristic selection are application specific, this general
020     * implementation provides items only for search strategy and mode selection.
021     * @author R. Lunde
022     */
023    public class MapAgentFrame extends AgentAppFrame {
024            public static String SCENARIO_SEL = "ScenarioSelection";
025            public static String DESTINATION_SEL = "DestinationSelection";
026            public static String AGENT_SEL = "AgentSelection";
027            public static String SEARCH_SEL = "SearchSelection";
028            public static String SEARCH_MODE_SEL = "SearchModeSelection";
029            public static String HEURISTIC_SEL = "HeuristicSelection";
030    
031            /** Standard constructor. */
032            public MapAgentFrame() {
033                    setSelectors(new String[]{
034                                    SCENARIO_SEL, DESTINATION_SEL, AGENT_SEL,
035                                    SEARCH_SEL, SEARCH_MODE_SEL, HEURISTIC_SEL},
036                                    new String[]{
037                                    "Select Scenario", "Select Destinations", "Select Agent",
038                                    "Select Search Strategy", "Select Search Mode", "Select Heuristic"}
039                    );
040                    setSelectorItems(SEARCH_SEL,
041                                    SearchFactory.getInstance().getSearchStrategyNames(), 5);
042                    setSelectorItems(SEARCH_MODE_SEL,
043                                    SearchFactory.getInstance().getSearchModeNames(), 0);
044                    setAgentView(new MapAgentView());
045                    setSplitPaneResizeWeight(0.75);
046                    setUpdateDelay(500);
047                    setSize(1000, 700);
048            }
049            
050            
051            //////////////////////////////////////////////////////////
052            // inner classes
053            
054            /**
055             * Agent view for map agent applications. This
056             * view requires the used model to be of type
057             * {@link MapAgentModel}.
058             */
059            class MapAgentView extends AgentView {
060                    
061                    /** Clears the panel and draws the map and the tour history. */
062                    public void paint(java.awt.Graphics g) {
063                            super.paint(g);
064                            MapAgentModel maModel = (MapAgentModel) model;
065                        if (maModel != null && !maModel.isEmpty()) {
066                            java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
067                            adjustTransformation();
068                            paintMap(g2);
069                            paintTour(g2);
070                            for (String loc : maModel.getLocations())
071                                    paintLoc(g2, loc);
072                        }
073                    }
074                    
075                    /**
076                     * Adjusts offsets and scale so that the whole map fits on the
077                     * view without scrolling.
078                     */
079                    private void adjustTransformation() {
080                            MapAgentModel maModel = (MapAgentModel) model;
081                            List<String> locs = maModel.getLocations();
082                    // adjust coordinates relative to the left upper corner of the graph area
083                    double minX = Double.POSITIVE_INFINITY;
084                    double minY = Double.POSITIVE_INFINITY;
085                    double maxX = Double.NEGATIVE_INFINITY;
086                    double maxY = Double.NEGATIVE_INFINITY;
087                    for (String loc : locs) {
088                            Point2D xy = maModel.getLocCoords(loc);
089                            if (xy.getX() < minX) minX = xy.getX();
090                            if (xy.getY() < minY) minY = xy.getY();
091                            if (xy.getX() > maxX) maxX = xy.getX();
092                            if (xy.getY() > maxY) maxY = xy.getY();
093                    }
094                    this.setBorder(20, 20, 20, 100);
095                    adjustTransformation(minX, minY, maxX, maxY);
096                    }
097                    
098                    /**
099                     * Represents roads by lines and locations by name-labeled points.
100                     */
101                    private void paintMap(java.awt.Graphics2D g2) {
102                            MapAgentModel maModel = (MapAgentModel) model;
103                            Map envMap = maModel.getEnvMap();
104                            Map agentMap = maModel.getAgentMap();
105                            List<Roadblock> roadblocks = new ArrayList<Roadblock>();
106                            for (String l1 : maModel.getLocations()) {
107                                    Point2D pt1 = maModel.getLocCoords(l1);
108                                    List<String> linkedLocs = envMap.getLocationsLinkedTo(l1);
109                                    for (String l2 : agentMap.getLocationsLinkedTo(l1))
110                                            if (!linkedLocs.contains(l2))
111                                                    linkedLocs.add(l2);
112                                    for (String l2 : linkedLocs) {
113                                            Point2D pt2 = maModel.getLocCoords(l2);
114                                            g2.setColor(Color.lightGray);
115                                    g2.drawLine(x(pt1), y(pt1), x(pt2), y(pt2));
116                                    boolean blockedInEnv =
117                                            !envMap.getLocationsLinkedTo(l2).contains(l1);
118                                    boolean blockedInAgent =
119                                            !agentMap.getLocationsLinkedTo(l2).contains(l1);
120                                    roadblocks.add(new Roadblock(pt1, pt2, blockedInEnv, blockedInAgent));
121                                    if (blockedInEnv && blockedInAgent) {
122                                            boolean blockedInEnvOtherDir =
123                                                    !envMap.getLocationsLinkedTo(l1).contains(l2);
124                                            boolean blockedInAgentOtherDir =
125                                                    !agentMap.getLocationsLinkedTo(l1).contains(l2);
126                                            roadblocks.add(new Roadblock(pt2, pt1, blockedInEnvOtherDir, blockedInAgentOtherDir));
127                                    }
128                                    }
129                            }
130                            for (Roadblock block : roadblocks)
131                                    paintRoadblock(g2, block);
132                    }
133                    
134                    /** The track of the agent is visualized with red lines. */
135                    private void paintTour(java.awt.Graphics2D g2) {
136                            MapAgentModel maModel = (MapAgentModel) model;
137                        Point2D lastPt = null;
138                        g2.setColor(Color.red);
139                        for (String loc : maModel.getTourHistory()) {
140                            Point2D pt = maModel.getLocCoords(loc);
141                            if (pt != null && lastPt != null) {
142                                    g2.drawLine(x(pt), y(pt), x(lastPt), y(lastPt));
143                            }
144                            lastPt = pt;
145                        }
146                    }
147                    
148                    /**
149                     * Roadblocks are represented by filled rectangles.
150                     * Blue denotes, the agent doesn't know it, red dentotes, there is
151                     * no roadblock, but the agent thinks so.  
152                     */
153                    private void paintRoadblock(java.awt.Graphics2D g2, Roadblock block) {
154                            if (block.inEnvMap || block.inAgentMap) {
155                                    int x = (int) (0.2*x(block.pos1)+0.8*x(block.pos2)-4);
156                                    int y = (int) (0.2*y(block.pos1)+0.8*y(block.pos2)-4);
157                                    if (!block.inAgentMap)
158                                            g2.setColor(Color.blue); // agent doesn't know the roadblock
159                                    else if (!block.inEnvMap)
160                                            g2.setColor(Color.red); // agent doesn't know the way
161                                    else
162                                            g2.setColor(Color.lightGray);
163                                    g2.fillRect(x, y, 9, 9);
164                            }
165                    }
166                    
167                private void paintLoc(java.awt.Graphics2D g2, String loc) {
168                    MapAgentModel maModel = (MapAgentModel) model;
169                            Point2D pt = maModel.getLocCoords(loc);
170                    if (pt != null) {
171                            int x = x(pt);
172                            int y = y(pt);
173                            String info = "";
174                            List<String> history = maModel.getTourHistory();
175                            ArrayList<Integer> list = new ArrayList<Integer>();
176                            for (int i =0; i < history.size(); i++)
177                                    if (history.get(i).equals(loc))
178                                            list.add(i+1);
179                            if (!list.isEmpty())
180                                    info = list.toString();
181                            if (maModel.hasObjects(loc)) {
182                                    g2.setColor(Color.green);
183                                    g2.fillOval(x-5, y-5, 10, 10);
184                            }
185                            if (maModel.isStart(loc)) {
186                                    g2.setColor(Color.red);
187                                    g2.fillOval(x-7, y-7, 14, 14);
188                            }
189                            if (!history.isEmpty() && loc.equals(history.get(history.size()-1))) {
190                                    g2.setColor(Color.red);
191                                    g2.fillOval(x-4, y-4, 8, 8);
192                            }
193                            if (maModel.hasInfos(loc)) {
194                                    g2.setColor(Color.blue);
195                                    g2.drawString("i", x, y+12);
196                            }
197                            //if (model.isStart(loc))
198                            //      g2.setColor(Color.red);
199                            //else
200                            if (maModel.isDestination(loc))
201                                    g2.setColor(Color.green);
202                            else if (history.contains(loc))
203                                    g2.setColor(Color.black);
204                            else
205                                    g2.setColor(Color.gray);
206                            g2.drawString(loc + info, x, y);
207                    }
208                }
209            }
210            
211            /**
212             * Stores roadblock information. Roadblocks are generally printed after
213             * the road itself so that they always appear in front.
214             */
215            private static class Roadblock {
216                    Point2D pos1;
217                    Point2D pos2;
218                    boolean inEnvMap;
219                    boolean inAgentMap;
220                    private Roadblock(Point2D pos1, Point2D pos2, boolean inEnvMap, boolean inAgentMap) {
221                            this.pos1 = pos1;
222                            this.pos2 = pos2;
223                            this.inEnvMap = inEnvMap;
224                            this.inAgentMap = inAgentMap;
225                    }
226        }
227    }