/*******************************************************************************
* TurtleKit 3 - Agent Based and Artificial Life Simulation Platform
* Copyright (C) 2011-2014 Fabien Michel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package turtlekit.viewer;
import static turtlekit.kernel.TurtleKit.Option.fastRendering;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import madkit.action.KernelAction;
import madkit.kernel.Madkit;
import madkit.simulation.probe.SingleAgentProbe;
import madkit.simulation.viewer.SwingViewer;
import turtlekit.agr.TKOrganization;
import turtlekit.gui.CudaMenu;
import turtlekit.gui.toolbar.TKToolBar;
import turtlekit.kernel.Patch;
import turtlekit.kernel.TKEnvironment;
import turtlekit.kernel.TKScheduler;
import turtlekit.kernel.TurtleKit;
import turtlekit.kernel.TurtleKit.Option;
/**
* A viewer that probes the patch grid and displays a representation for it.
* It provides a zoom feature.
*
* @author Fabien Michel
* @since TurtleKit 3.0.0.4
* @version 0.1
*
*/
public abstract class AbstractGridViewer extends SwingViewer {
/**
*
*/
private static final long serialVersionUID = 671181979144051600L;
protected int cellSize = 10;
private SingleAgentProbe<TKEnvironment, Patch[]> gridProbe;
public SingleAgentProbe<TKEnvironment, Patch[]> getGridProbe() {
return gridProbe;
}
private SingleAgentProbe<TKScheduler,Double> timeProbe;
private SingleAgentProbe<TKEnvironment, Integer> widthProbe;
private SingleAgentProbe<TKEnvironment, Integer> heightProbe;
private JPanel turtlePane;
protected void initProbes(){
gridProbe = new SingleAgentProbe<TKEnvironment, Patch[]>(
getCommunity(),
TKOrganization.MODEL_GROUP,
TKOrganization.ENVIRONMENT_ROLE,
"patchGrid");
addProbe(gridProbe);
timeProbe = new SingleAgentProbe<>(getCommunity(), TKOrganization.ENGINE_GROUP, TKOrganization.SCHEDULER_ROLE,"GVT");
addProbe(timeProbe);
widthProbe = new SingleAgentProbe<TKEnvironment, Integer>(
getCommunity(),
TKOrganization.MODEL_GROUP,
TKOrganization.ENVIRONMENT_ROLE,
"width");
addProbe(widthProbe);
heightProbe = new SingleAgentProbe<TKEnvironment, Integer>(
getCommunity(),
TKOrganization.MODEL_GROUP,
TKOrganization.ENVIRONMENT_ROLE,
"height");
addProbe(heightProbe);
}
public AbstractGridViewer() {
createGUIOnStartUp();
// setLogLevel(Level.ALL);
}
@Override
protected void activate() {
final int parseInt = Integer.parseInt(getMadkitProperty(Option.renderingInterval));
if(parseInt != 1){
setRenderingInterval(parseInt);
}
setSynchronousPainting(! isMadkitPropertyTrue(fastRendering));
requestRole(getCommunity(), TKOrganization.ENGINE_GROUP,TKOrganization.VIEWER_ROLE);
}
@Override
protected void end() {
if(getAgentsWithRole(getCommunity(), TKOrganization.ENGINE_GROUP,TKOrganization.VIEWER_ROLE) == null){
KernelAction.EXIT.getActionFor(this).actionPerformed(null);
}
}
// private void handleMessage(Message m){
// if(m instanceof DimensionMessage)
// handleDimensionMessage((DimensionMessage) m);
// }
//
// private void handleDimensionMessage(DimensionMessage m) {
// Dimension d = m.getContent();
// width = (int) d.getWidth();
// height = (int) d.getHeight();
// updateSize();
// }
@Override
public void setupFrame(final JFrame frame) {
initProbes();
SingleAgentProbe<TKScheduler,Double> p = new SingleAgentProbe<>(getCommunity(), TKOrganization.ENGINE_GROUP, TKOrganization.SCHEDULER_ROLE,"GVT");
addProbe(p);
final TKScheduler tkScheduler = p.getCurrentAgentsList().get(0);
final JToolBar schedulerToolBar = tkScheduler.getSchedulerToolBar();
super.setupFrame(frame);
getFrame().getJMenuBar().add(new CudaMenu(getCurrentEnvironment()));
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
//because there may be several viewers
if (getAgentsWithRole(getCommunity(), TKOrganization.ENGINE_GROUP, TKOrganization.VIEWER_ROLE) == null) {
KernelAction.EXIT.getActionFor(AbstractGridViewer.this).actionPerformed(null);
}
}
});
frame.remove(getToolBar());
final TKToolBar comp = new TKToolBar(this);
comp.addSeparator();
comp.add(getToolBar());
comp.addSeparator();
// schedulerToolBar.setFloatable(false);
comp.add(schedulerToolBar);
// comp.setOrientation(JToolBar.VERTICAL);
frame.add(comp,BorderLayout.PAGE_START);
comp.add(tkScheduler.getGVTLabel());
checkFrameSize();
getDisplayPane().setBackground(Color.BLACK);
getDisplayPane().addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
// cellSize -= e.getWheelRotation();
// if(cellSize < 1)
// cellSize = 1;
// receiveMessage(new DimensionMessage(width,height));
updateSize(e.getPoint(),e.getWheelRotation());
// Graphics2D g2 = (Graphics2D) frame.getGraphics();
// int w = getDisplayPane().getWidth();// real width of canvas
// int h = getDisplayPane().getHeight();// real height of canvas
// // Translate used to make sure scale is centered
// g2.translate(w/2, h/2);
// g2.scale(2, 2);
// g2.translate(-w/2, -h/2);
// Point pt = turtlePane.getLocation();
// turtlePane.setLocation(pt.x-e.getX(),pt.y - e.getY());
// pt = turtlePane.getLocation();
// System.err.println(pt);
}
});
turtlePane = (JPanel) getDisplayPane();
setDisplayPane(new JScrollPane(turtlePane));
updateSize(null);
frame.pack();
frame.setLocationRelativeTo(null);
schedulerToolBar.getComponent(0).requestFocus();
}
/**
*
*/
private void checkFrameSize() {
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double screenW = screenSize.getWidth();
double screenH = screenSize.getHeight();
while((getWidth()*cellSize > screenW - 200 || getHeight()*cellSize > screenH-300) && cellSize > 1){
cellSize--;
}
// getFrame().setSize(getWidth()*cellSize, getHeight()*cellSize);
}
/**
* @param point
* @param wheelRotation
*/
private void updateSize(Point point, int wheelRotation) {
final int i = point.x / cellSize;
int offX = (int) i * wheelRotation;
int offY = (int) (point.y / cellSize) * wheelRotation;
cellSize -= wheelRotation;
if (cellSize < 1)
cellSize = 1;
if (cellSize > 1) {
turtlePane.setLocation(turtlePane.getLocation().x + offX,
turtlePane.getLocation().y + offY);
}
else{
turtlePane.setLocation(0, 0);
}
turtlePane.setPreferredSize(new Dimension(getWidth() * cellSize, getHeight() * cellSize));
// checkFrameSize();
turtlePane.getParent().doLayout();
// getFrame().setSize(
// new Dimension(getWidth() * cellSize + 100, getHeight()
// * cellSize + 100));
// getFrame().pack();
}
private void updateSize(Point p) {
turtlePane.setPreferredSize(new Dimension(getWidth() * cellSize,
getHeight() * cellSize));
if (p != null) {
// System.err.println("p" + p);
// System.err.println("location " + turtlePane.getLocation());
int offX = (int) (p.x * cellSize) - p.x;
int offY = (int) (p.y * cellSize) - p.y;
// System.err.println("offx " + offX);
// System.err.println("offy " + offY);
// System.err.println("new loc " + turtlePane.getLocation());
turtlePane.setLocation(turtlePane.getLocation().x - offX,
turtlePane.getLocation().y - offY);
}
else{
// getDisplayPane().setPreferredSize(new Dimension(getWidth() * cellSize,
// getHeight() * cellSize));
getFrame().setSize(
new Dimension(getWidth() * cellSize + 100, getHeight()
* cellSize + 100));
getFrame().pack();
}
// if (Toolkit.getDefaultToolkit().getScreenSize().getHeight() < getHeight()
// * cellSize) {
// }
// turtlePane.getParent().doLayout();
// System.err.println("final loc " + turtlePane.getLocation());
}
/**
* @return
*/
public Patch[] getPatchGrid() {
return gridProbe.getPropertyValue();
}
public Patch getPatch(int x, int y){
return gridProbe.getPropertyValue()[x + getWidth() * y];
}
/**
* @return
*/
public int getHeight() {
return heightProbe.getPropertyValue();
}
/**
* @return
*/
public int getWidth() {
return widthProbe.getPropertyValue();
}
public TKEnvironment getCurrentEnvironment(){
return heightProbe.getProbedAgent();
}
public double getSimulationTime(){
return timeProbe.getPropertyValue();
}
/**
* @return the cellSize
*/
public int getCellSize() {
return cellSize;
}
/**
* @param cellSize the cellSize to set
*/
public void setCellSize(int cellSize) {
this.cellSize = cellSize;
}
/**
* shortcut for getMadkitProperty(TurtleKit.Option.community)
*
* @return the community of the simulation this agent is in
*/
public final String getCommunity() {
return getMadkitProperty(TurtleKit.Option.community);
}
/**
* This offers a convenient way to create a main method
* that launches a simulation using the viewer
* class under development.
* This call only works in the main method of the viewer.
*
* @param args
* MaDKit or TurtleKit options
* @see #executeThisAgent(int, boolean, String...)
* @since TurtleKit 3.0.0.1
*/
protected static void executeThisViewer(String... args) {
StackTraceElement element = null;
for (StackTraceElement stackTraceElement : new Throwable().getStackTrace()) {
if(stackTraceElement.getMethodName().equals("main")){
element = stackTraceElement;
break;
}
}
final ArrayList<String> arguments = new ArrayList<>(Arrays.asList(
Madkit.BooleanOption.desktop.toString(),"false",
Madkit.Option.configFile.toString(), "turtlekit/kernel/turtlekit.properties",
TurtleKit.Option.viewers.toString(), element.getClassName()));
if (args != null) {
arguments.addAll(Arrays.asList(args));
}
new Madkit(arguments.toArray(new String[0]));
}
}