1320 lines
34 KiB
Java
1320 lines
34 KiB
Java
package org.artisanlogiciel.games;
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.image.BufferedImage;
|
|
import java.awt.Color;
|
|
import java.awt.Container;
|
|
import java.awt.Dimension;
|
|
import java.awt.FlowLayout;
|
|
import java.awt.Graphics;
|
|
import java.awt.Point;
|
|
import java.awt.Rectangle;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.ComponentAdapter;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.InputEvent;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseMotionListener;
|
|
import java.io.DataOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.LinkedList;
|
|
import java.util.Locale;
|
|
import java.util.Scanner;
|
|
import java.util.ResourceBundle;
|
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
import javax.swing.AbstractAction;
|
|
import javax.swing.Action;
|
|
import javax.swing.JButton;
|
|
import javax.swing.JComponent;
|
|
import javax.swing.JCheckBox;
|
|
import javax.swing.JFrame;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JScrollPane;
|
|
import javax.swing.JSlider;
|
|
import javax.swing.JTextField;
|
|
import javax.swing.KeyStroke;
|
|
import javax.swing.event.ChangeEvent;
|
|
import javax.swing.event.ChangeListener;
|
|
|
|
import org.artisanlogiciel.games.stl.Wall3d;
|
|
import org.artisanlogiciel.util.UTF8Control;
|
|
|
|
import org.artisanlogiciel.graphics.Drawing;
|
|
import org.artisanlogiciel.graphics.DrawingLine;
|
|
import org.artisanlogiciel.graphics.SvgWriter;
|
|
|
|
/**
|
|
* Display is Main JFrame for this tool
|
|
**/
|
|
public class Display extends JFrame
|
|
{
|
|
// to please eclipse, not supposed to be serialized
|
|
private static final long serialVersionUID = 8500214871372184418L;
|
|
|
|
final static ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", Locale.getDefault(), Display.class.getClassLoader(), new UTF8Control());
|
|
|
|
MazeComponent maze;
|
|
MazeControler controler;
|
|
LabyModel model;
|
|
JPanel controlPanel;
|
|
boolean autoSize;
|
|
|
|
MazeParams params = null;
|
|
|
|
Drawing drawing = new Drawing();
|
|
|
|
Display(LabyModel model,int W, int H, MazeParams params)
|
|
{
|
|
super(labels.getString("title"));
|
|
if (params != null)
|
|
{
|
|
// fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth());
|
|
this.params = params;
|
|
}
|
|
this.model = model;
|
|
maze = createMazeComponent(model,W,H);
|
|
|
|
Container con = this.getContentPane();
|
|
JScrollPane scrollableMaze = new JScrollPane(maze);
|
|
scrollableMaze.addMouseMotionListener(maze);
|
|
con.add(scrollableMaze, BorderLayout.CENTER);
|
|
controler = new MazeControler(params);
|
|
con.add(controler.getMoveControl(), BorderLayout.NORTH);
|
|
con.add(controler.getGenerationControl(), BorderLayout.SOUTH);
|
|
|
|
addComponentListener(new ComponentAdapter() {
|
|
@Override
|
|
public void componentResized(ComponentEvent e) {
|
|
if ( autoSize )
|
|
{
|
|
maze.getAutoSize();
|
|
}
|
|
}
|
|
});
|
|
|
|
model.setMazeListener(maze);
|
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
setBounds(W, H, W, H);
|
|
setVisible(true);
|
|
}
|
|
|
|
private static MazeComponent createMazeComponent(LabyModel model, int W, int H)
|
|
{
|
|
MazeCellParameters cp = new MazeCellParameters(model.getWidth(),model.getHeight(), W, H, 3, 3);
|
|
MazeComponent comp = new MazeComponent(model, cp);
|
|
return comp;
|
|
}
|
|
|
|
void recreateModel()
|
|
{
|
|
// recreate labyrinth
|
|
if (params != null)
|
|
{
|
|
params = controler.getSettings().getParams();
|
|
// keep current model...
|
|
// model = new LabyModel(params, new java.util.Random());
|
|
maze.resetWallsProvider(model);
|
|
model.setMazeListener(maze);
|
|
|
|
model.reset();
|
|
|
|
// we are in GUI event thread, need to release it and do it outside.
|
|
new Thread() {
|
|
public void run() {
|
|
model.generateWithEntry(0, 0);
|
|
model.addEntryOrExit(-1, 0);
|
|
model.addEntryOrExit(params.getWidth(), params.getHeight() - 1);
|
|
}
|
|
}.start();
|
|
}
|
|
}
|
|
|
|
void resetModel()
|
|
{
|
|
// recreate labyrinth
|
|
if (params != null)
|
|
{
|
|
params = controler.getSettings().getParams();
|
|
model = new LabyModel(params, new java.util.Random());
|
|
maze.resetWallsProvider(model);
|
|
model.setMazeListener(maze);
|
|
|
|
|
|
// we are in GUI event thread, need to release it and do it outside.
|
|
new Thread() {
|
|
public void run() {
|
|
maze.changed(null,null,model);
|
|
}
|
|
}.start();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
void resolve()
|
|
{
|
|
model.reset();
|
|
model.resolve(model.getWidth() - 1, model.getHeight() - 1, maze);
|
|
}
|
|
|
|
void goNorth()
|
|
{
|
|
maze.goNorth();
|
|
}
|
|
|
|
void goSouth()
|
|
{
|
|
maze.goSouth();
|
|
}
|
|
|
|
void goEast()
|
|
{
|
|
maze.goEast();
|
|
}
|
|
|
|
void goWest()
|
|
{
|
|
maze.goWest();
|
|
}
|
|
|
|
void setWallSize(int size)
|
|
{
|
|
maze.setWallSize(size);
|
|
}
|
|
|
|
void setAutoSize(boolean autoSize)
|
|
{
|
|
this.autoSize = autoSize;
|
|
if ( autoSize )
|
|
{
|
|
maze.getAutoSize();
|
|
}
|
|
}
|
|
|
|
void addWallInDrawing(int pX, int pY, Drawing d)
|
|
{
|
|
short walls = model.getWalls(pX,pY);
|
|
// todo
|
|
int w = 2;
|
|
int h = 2;
|
|
int ox=0;
|
|
int oy=0;
|
|
|
|
int x = ox + (int) (pX * w);
|
|
int y = oy + (int) (pY * h);
|
|
DrawingLine dl = new DrawingLine();
|
|
if ( (pY == 0) && ((walls & Brick.UP) == Brick.UP))
|
|
{
|
|
dl.addPoint(new Point(x, y));
|
|
dl.addPoint(new Point(x + (int) w, y));
|
|
}
|
|
if ((walls & Brick.DOWN) == Brick.DOWN)
|
|
{
|
|
dl.addPoint(new Point(x, y + (int) h));
|
|
dl.addPoint(new Point(x + (int) w, y + (int) h));
|
|
}
|
|
if ((walls & Brick.RIGHT) == Brick.RIGHT)
|
|
{
|
|
dl.addPoint(new Point(x + (int) w, y));
|
|
dl.addPoint(new Point(x + (int) w, y + (int) h));
|
|
}
|
|
if ( (pX == 0 ) && ((walls & Brick.LEFT) == Brick.LEFT))
|
|
{
|
|
dl.addPoint(new Point(x, y));
|
|
dl.addPoint(new Point(x, y + (int) h));
|
|
}
|
|
d.addLine(dl);
|
|
}
|
|
|
|
Drawing createDrawing()
|
|
{
|
|
Drawing d = new Drawing();
|
|
{
|
|
short walls;
|
|
|
|
// draw all walls within clip bounds horiz first then lines
|
|
for (int y = 0; y < model.getHeight(); y++)
|
|
{
|
|
for (int x = 0; x < model.getWidth(); x ++)
|
|
{
|
|
addWallInDrawing(x,y,d);
|
|
}
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
// to allow to log / write somewher into screen...
|
|
void writeSentence(String pSentence)
|
|
{
|
|
// TODO
|
|
System.out.println( pSentence);
|
|
}
|
|
|
|
void writeError(String pError)
|
|
{
|
|
System.err.println(pError);
|
|
}
|
|
|
|
void saveImc()
|
|
{
|
|
Drawing d = createDrawing();
|
|
|
|
if ( d != null )
|
|
{
|
|
File outfile = new File(params.getSaveDir(), params.getName() + ".imc");
|
|
writeSentence("Saving to " + outfile + " ...");
|
|
try
|
|
{
|
|
DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile));
|
|
d.saveLinesKompressed(out);
|
|
out.flush();
|
|
out.close();
|
|
writeSentence("... Done.");
|
|
}
|
|
catch (IOException io)
|
|
{
|
|
io.printStackTrace(System.err);
|
|
}
|
|
}
|
|
}
|
|
|
|
void saveSvg()
|
|
{
|
|
Drawing d = createDrawing();
|
|
|
|
if ( d != null )
|
|
{
|
|
File outfile = new File(params.getSaveDir(), params.getName() + ".svg");
|
|
writeSentence("Saving to " + outfile + " ...");
|
|
try
|
|
{
|
|
DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile));
|
|
SvgWriter writer = new SvgWriter(d.getInternLines());
|
|
writer.writeTo(out);
|
|
out.flush();
|
|
out.close();
|
|
writeSentence("... Done.");
|
|
}
|
|
catch (IOException io)
|
|
{
|
|
io.printStackTrace(System.err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writeError("drawing creation failed");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void savePng()
|
|
{
|
|
File file = new File("snapshot.png");
|
|
// BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB);
|
|
BufferedImage bi = new BufferedImage(maze.getSize().width, maze.getSize().height, BufferedImage.TYPE_INT_ARGB);
|
|
Graphics g = bi.createGraphics();
|
|
// this.paint(g);
|
|
maze.paint(g);
|
|
g.dispose();
|
|
try{
|
|
ImageIO.write(bi,"png",file);
|
|
}catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private class MazeSettings extends JPanel
|
|
{
|
|
MazeParams params;
|
|
|
|
JTextField textWidth = null;
|
|
JTextField textHeight = null;
|
|
JTextField textDepth = null;
|
|
|
|
// TODO set width and height and depth of maze with gui
|
|
public MazeSettings(MazeParams params)
|
|
{
|
|
super();
|
|
this.params = params;
|
|
createSettingsGui();
|
|
}
|
|
|
|
void createSettingsGui()
|
|
{
|
|
if ( params != null )
|
|
{
|
|
JButton buttonCreate = new JButton(labels.getString("create"));
|
|
buttonCreate.addActionListener(new ActionListener()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
recreateModel();
|
|
}
|
|
});
|
|
add(buttonCreate);
|
|
|
|
JButton buttonReset = new JButton("reset");//labels.getString("reset"));
|
|
buttonReset.addActionListener(new ActionListener()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
resetModel();
|
|
}
|
|
});
|
|
add(buttonReset);
|
|
|
|
}
|
|
if ( params != null )
|
|
{
|
|
JLabel widthLabel = new JLabel(labels.getString("width"));
|
|
textWidth = new JTextField("0" + params.getWidth());
|
|
add(widthLabel);
|
|
add(textWidth);
|
|
JLabel heightLabel = new JLabel(labels.getString("height"));
|
|
textHeight = new JTextField("0" + params.getHeight());
|
|
add(heightLabel);
|
|
add(textHeight);
|
|
JLabel depthLabel = new JLabel(labels.getString("depth"));
|
|
textDepth = new JTextField("0" + params.getMaxDepth());
|
|
add(depthLabel);
|
|
add(textDepth);
|
|
}
|
|
}
|
|
|
|
public MazeParams getParams()
|
|
{
|
|
return new MazeParamsFixed(params.getSaveDir(),
|
|
Integer.parseInt(textWidth.getText()),
|
|
Integer.parseInt(textHeight.getText()),
|
|
Integer.parseInt(textDepth.getText()));
|
|
}
|
|
|
|
}
|
|
|
|
private static class MazeParamsFixed implements MazeParams
|
|
{
|
|
int width;
|
|
int height;
|
|
int maxdepth;
|
|
File labdir;
|
|
String name;
|
|
|
|
public MazeParamsFixed()
|
|
{
|
|
}
|
|
|
|
public MazeParamsFixed(MazeParams params)
|
|
{
|
|
setParams(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth());
|
|
}
|
|
|
|
public void setParams(File saveDir, int W, int H, int MD)
|
|
{
|
|
labdir = saveDir;
|
|
width=W;
|
|
height=H;
|
|
maxdepth=MD;
|
|
}
|
|
|
|
public MazeParamsFixed(File saveDir, int W, int H, int MD)
|
|
{
|
|
name = null;
|
|
setParams(saveDir,W,H,MD);
|
|
}
|
|
|
|
public int getWidth()
|
|
{
|
|
return width;
|
|
}
|
|
|
|
public int getHeight()
|
|
{
|
|
return height;
|
|
}
|
|
|
|
public int getMaxDepth()
|
|
{
|
|
return maxdepth;
|
|
}
|
|
|
|
public void setName(String n)
|
|
{
|
|
name = n;
|
|
}
|
|
|
|
public String getName()
|
|
{
|
|
if (name == null)
|
|
{
|
|
name = "lab" + width + "x" + height;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
public File getSaveDir()
|
|
{
|
|
return labdir;
|
|
}
|
|
}
|
|
|
|
|
|
private class MazeControler extends JPanel
|
|
{
|
|
/**
|
|
*
|
|
*/
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private MazeSettings settings = null;
|
|
|
|
private JPanel resizecontrol = null;
|
|
|
|
public MazeControler(MazeParams params)
|
|
{
|
|
super(new BorderLayout());
|
|
controlPanel = new JPanel(new BorderLayout());
|
|
settings = new MazeSettings(params);
|
|
JButton button = new JButton(labels.getString("resolve"));
|
|
button.addActionListener(new ActionListener()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("Resolving");
|
|
resolve();
|
|
}
|
|
});
|
|
|
|
@SuppressWarnings("serial")
|
|
Action goNorth = new AbstractAction()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("go North");
|
|
goNorth();
|
|
}
|
|
};
|
|
JButton north = addDirection(this, labels.getString("north"), "UP", goNorth);
|
|
|
|
@SuppressWarnings("serial")
|
|
Action goEast = new AbstractAction()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("go East");
|
|
goEast();
|
|
}
|
|
};
|
|
|
|
JButton east = addDirection(this, labels.getString("east"), "RIGHT", goEast);
|
|
|
|
@SuppressWarnings("serial")
|
|
Action goWest = new AbstractAction()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("go West");
|
|
goWest();
|
|
}
|
|
};
|
|
JButton west = addDirection(this, labels.getString("west"), "LEFT", goWest);
|
|
|
|
@SuppressWarnings("serial")
|
|
Action goSouth = new AbstractAction()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("go South");
|
|
goSouth();
|
|
}
|
|
};
|
|
JButton south = addDirection(this, labels.getString("south"), "DOWN", goSouth);
|
|
|
|
controlPanel.add(button, BorderLayout.CENTER);
|
|
controlPanel.add(north, BorderLayout.NORTH);
|
|
controlPanel.add(west, BorderLayout.WEST);
|
|
controlPanel.add(east, BorderLayout.EAST);
|
|
controlPanel.add(south, BorderLayout.SOUTH);
|
|
add(controlPanel, BorderLayout.NORTH);
|
|
|
|
// control display panel contains controls for display
|
|
// zoom , autozoom.
|
|
final JPanel controlDisplayPanel = new JPanel(new BorderLayout());
|
|
final JCheckBox showAll = new JCheckBox("show all");
|
|
showAll.addChangeListener(new ChangeListener()
|
|
{
|
|
public void stateChanged(ChangeEvent e)
|
|
{
|
|
maze.setShowAll( showAll.isSelected());
|
|
}
|
|
});
|
|
|
|
final JSlider slider = new JSlider(2, 40);
|
|
slider.addChangeListener(new ChangeListener()
|
|
{
|
|
public void stateChanged(ChangeEvent e)
|
|
{
|
|
setWallSize(slider.getValue());
|
|
}
|
|
});
|
|
final JCheckBox autoSlide = new JCheckBox("autoslide");
|
|
autoSlide.addChangeListener(new ChangeListener()
|
|
{
|
|
public void stateChanged(ChangeEvent e)
|
|
{
|
|
setAutoSize( autoSlide.isSelected());
|
|
}
|
|
});
|
|
final JTextField saveName = new JTextField("newlaby ");
|
|
final JButton savePngButton = new JButton(labels.getString("save") +" png");
|
|
Action savePngAction = new AbstractAction() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("save png");
|
|
savePng();
|
|
}
|
|
};
|
|
savePngButton.addActionListener(savePngAction);
|
|
final JButton saveSvgButton = new JButton(labels.getString("save") +" svg");
|
|
Action saveSvgAction = new AbstractAction() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
writeSentence("save png");
|
|
saveSvg();
|
|
}
|
|
};
|
|
saveSvgButton.addActionListener(saveSvgAction);
|
|
final JButton saveButton = new JButton(labels.getString("save") +" raw");
|
|
Action saveAction = new AbstractAction() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("save");
|
|
MazeParamsFixed p = (MazeParamsFixed) params;
|
|
p.setName(saveName.getText());
|
|
save(p,model);
|
|
}
|
|
};
|
|
saveButton.addActionListener(saveAction);
|
|
final JButton saveImcButton = new JButton(labels.getString("save") +" imc");
|
|
Action saveImcAction = new AbstractAction() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("save imc");
|
|
/*
|
|
MazeParamsFixed p = (MazeParamsFixed) params;
|
|
p.setName(saveName.getText());
|
|
save(p,model);
|
|
*/
|
|
saveImc();
|
|
}
|
|
};
|
|
saveImcButton.addActionListener(saveImcAction);
|
|
|
|
final JButton quitButton = new JButton(labels.getString("quit"));
|
|
Action quitAction = new AbstractAction() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
//
|
|
System.out.println("quit");
|
|
System.exit(0);
|
|
}
|
|
};
|
|
quitButton.addActionListener(quitAction);
|
|
|
|
resizecontrol = new JPanel(new FlowLayout());
|
|
resizecontrol.add(showAll);
|
|
resizecontrol.add(slider);
|
|
resizecontrol.add(autoSlide);
|
|
|
|
// todo dedicate a panel for save
|
|
JPanel savePanel = resizecontrol;
|
|
savePanel.add(saveName);
|
|
savePanel.add(saveSvgButton);
|
|
savePanel.add(savePngButton);
|
|
savePanel.add(saveButton);
|
|
savePanel.add(saveImcButton);
|
|
|
|
resizecontrol.add(quitButton);
|
|
|
|
add(controlDisplayPanel, BorderLayout.SOUTH);
|
|
add(resizecontrol,BorderLayout.WEST);
|
|
add(settings,BorderLayout.EAST);
|
|
}
|
|
|
|
private JPanel getMoveControl()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
private JPanel getResizeControl()
|
|
{
|
|
return resizecontrol;
|
|
}
|
|
|
|
private JPanel getGenerationControl()
|
|
{
|
|
return settings;
|
|
}
|
|
|
|
public MazeSettings getSettings()
|
|
{
|
|
return settings;
|
|
}
|
|
|
|
}
|
|
|
|
private JButton addDirection(final JPanel panel, final String direction, String key, Action goAction)
|
|
{
|
|
String actionName = "go" + direction;
|
|
JButton button = new JButton(direction);
|
|
button.addActionListener(goAction);
|
|
KeyStroke keystroke = KeyStroke.getKeyStroke(key);
|
|
// attach keys to maze
|
|
maze.getInputMap().put(keystroke, actionName);
|
|
maze.getActionMap().put(actionName, goAction);
|
|
return button;
|
|
}
|
|
|
|
private static class MazeCellParameters
|
|
{
|
|
double width = 10; // width of one cell
|
|
double height = 10; // height of one cell
|
|
int offsetX = 5; // x offset of upper corner left
|
|
int offsetY = 5; // y offset of upper corner left
|
|
|
|
int mapWidth = 0;
|
|
int mapHeight = 0;
|
|
|
|
public MazeCellParameters(int mapw, int maph, int W, int H, int x, int y)
|
|
{
|
|
double w = ( W - x )/ mapw;
|
|
double h = ( H - y )/ maph;
|
|
mapWidth = mapw;
|
|
mapHeight = maph;
|
|
if (w < 5)
|
|
w = 5;
|
|
if (h < 5)
|
|
h = 5;
|
|
setCellSize(w,h);
|
|
offsetX = x;
|
|
offsetY = y;
|
|
}
|
|
|
|
public void resetMazeWidthHeight(int mapw, int maph)
|
|
{
|
|
mapWidth = mapw;
|
|
mapHeight = maph;
|
|
}
|
|
|
|
public void adaptTo(double W, double H)
|
|
{
|
|
double w = ( W - offsetX ) / mapWidth;
|
|
double h = ( H - offsetY ) / mapHeight;
|
|
mapWidth = mapWidth;
|
|
mapHeight = mapHeight;
|
|
if (w < 5)
|
|
w = 5;
|
|
if (h < 5)
|
|
h = 5;
|
|
setCellSize(w,h);
|
|
}
|
|
|
|
public void setCellSize(double w, double h)
|
|
{
|
|
width = w;
|
|
height = h;
|
|
}
|
|
|
|
public double getWidth()
|
|
{
|
|
return width;
|
|
}
|
|
|
|
public double getHeight()
|
|
{
|
|
return height;
|
|
}
|
|
|
|
public int getOffsetX()
|
|
{
|
|
return offsetX;
|
|
}
|
|
|
|
public int getOffsetY()
|
|
{
|
|
return offsetY;
|
|
}
|
|
|
|
public Dimension getDimension()
|
|
{
|
|
return new Dimension(offsetX + (int) (mapWidth * width),
|
|
offsetY + (int) (mapHeight * height));
|
|
}
|
|
|
|
public void drawWalls(Graphics g, int pX, int pY, short walls)
|
|
{
|
|
int x = offsetX + (int) (pX * width);
|
|
int y = offsetY + (int) (pY * height);
|
|
if ( (pY == 0) && ((walls & Brick.UP) == Brick.UP))
|
|
g.drawLine(x, y, x + (int) width, y);
|
|
if ((walls & Brick.DOWN) == Brick.DOWN)
|
|
g.drawLine(x, y + (int) height, x + (int) width, y + (int) height);
|
|
if ((walls & Brick.RIGHT) == Brick.RIGHT)
|
|
g.drawLine(x + (int) width, y, x + (int) width, y + (int) height);
|
|
if ( (pX == 0 ) && ((walls & Brick.LEFT) == Brick.LEFT))
|
|
g.drawLine(x, y, x, y + (int) height);
|
|
}
|
|
|
|
public void drawPath(Graphics g, DirectionPosition dp, int pX, int pY, int mX, int mY)
|
|
{
|
|
if ( dp != null )
|
|
{
|
|
Position dot = dp.getPosition();
|
|
if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY))
|
|
{
|
|
int x = offsetX + (int) (dot.getX() * width);
|
|
int y = offsetY + (int) (dot.getY() * height);
|
|
short path = dp.getDirection();
|
|
int xm = x+ (int) (width / 2);
|
|
int ym = y+ (int) (height / 2);
|
|
if ( (path & LabyModel.HORIZONTAL) == LabyModel.HORIZONTAL)
|
|
{
|
|
if ( (path & LabyModel.POSITIVE) == LabyModel.POSITIVE )
|
|
{
|
|
g.drawLine(xm, ym, x + (int) width, ym);
|
|
g.drawLine(xm, ym + (int) (height / 4), x + (int) width, ym);
|
|
}
|
|
// LEFT /_
|
|
if ( (path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE )
|
|
{
|
|
g.drawLine(x, ym, xm, ym);
|
|
g.drawLine(x, ym, xm, ym - (int) (height / 4));
|
|
}
|
|
}
|
|
if ((path & LabyModel.VERTICAL) == LabyModel.VERTICAL)
|
|
{
|
|
if ( (path & LabyModel.POSITIVE) == LabyModel.POSITIVE )
|
|
{
|
|
g.drawLine(xm, ym, xm , y + (int) height);
|
|
g.drawLine(xm - (int) (width / 4), ym , xm, y + (int) height);
|
|
}
|
|
// UP |\
|
|
if ( (path & LabyModel.NEGATIVE) == LabyModel.NEGATIVE )
|
|
{
|
|
g.drawLine(xm, ym, xm , y);
|
|
g.drawLine(xm + (int) (width /4), ym , xm , y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void drawDot(Graphics g, Position dot, int pX, int pY, int mX, int mY)
|
|
{
|
|
double radius = (height > width) ? width : height;
|
|
int a = (int) ( radius / 4 );
|
|
if ((dot.getX() >= pX) && (dot.getY() >= pY) && (dot.getX() < mX) && (dot.getY() < mY))
|
|
{
|
|
int x = offsetX + (int) (dot.getX() * width);
|
|
int y = offsetY + (int) (dot.getY() * height);
|
|
int r2 = (int) ( (radius * 3)/4 );
|
|
g.drawOval(x + 1, y + 1, r2, r2);
|
|
// g.drawLine(x+a,y+a,x+width-a,y+height-a);
|
|
// g.drawLine(x+a,y+height-a,x+width-a,y+a);
|
|
|
|
}
|
|
else
|
|
{
|
|
int x = offsetX + (int) (pX * width);
|
|
int y = offsetY + (int) (pY * height);
|
|
g.drawLine(x + 1, y + 1, x + (int) width - 1, y + (int) height - 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private static class MazeComponent
|
|
extends JComponent
|
|
implements MazeCreationListener,
|
|
MazeResolutionListener,
|
|
MouseMotionListener
|
|
{
|
|
private static final long serialVersionUID = 3163272907991176390L;
|
|
|
|
// WallsProvider map;
|
|
LabyModel map;
|
|
final MazeCellParameters cp;
|
|
Position current = null;
|
|
LinkedList<DirectionPosition> solvedPath = null;
|
|
LinkedList<DirectionPosition> drawingPath = null;
|
|
final Object lockChange = new Object();
|
|
// current postion of human resolving
|
|
int sX = 0;
|
|
int sY = 0;
|
|
// goal exit
|
|
int gX = -1;
|
|
int gY = -1;
|
|
|
|
// not set by default for debug, will show any path
|
|
boolean showAll = false;
|
|
|
|
// for a given (x,y) pixel return cell position.
|
|
Position getPosition(int x, int y)
|
|
{
|
|
int pX = (int) ((double) (x - cp.getOffsetX()) / cp.getWidth());
|
|
int pY = (int) ((double) (y - cp.getOffsetY()) / cp.getHeight());
|
|
return new Position(pX,pY);
|
|
}
|
|
|
|
@Override
|
|
public void mouseDragged(MouseEvent e) {
|
|
// should find the cell ...
|
|
DirectionPosition last = null;
|
|
short path = 0;
|
|
Position newPosition = getPosition(e.getX(),e.getY());
|
|
if (drawingPath == null )
|
|
{
|
|
drawingPath = new LinkedList<>();
|
|
last = new DirectionPosition((short) 0,newPosition);
|
|
System.out.println("Mouse dragged Cell " + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK);
|
|
drawingPath.addLast(last);
|
|
}
|
|
else
|
|
{
|
|
// setShowAll(true);
|
|
DirectionPosition first = drawingPath.getLast();
|
|
last = first;
|
|
// construct as many move form last to new position as needed.
|
|
while ( ! last.getPosition().equals(newPosition))
|
|
{
|
|
path=LabyModel.getDirection(last.getPosition(),newPosition);
|
|
last.setDirection(path);
|
|
// button 1 : add direction; button 2 : replace with direction.
|
|
if ( ( e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK ) != 0 )
|
|
{
|
|
map.addDirection(last.getPosition().getX(),last.getPosition().getY(),path);
|
|
}
|
|
else
|
|
{
|
|
map.setDirection(last.getPosition().getX(),last.getPosition().getY(),path);
|
|
}
|
|
last = last.moveToAdjacentDirection();
|
|
drawingPath.addLast(last);
|
|
}
|
|
System.out.println("Mouse dragged from Cell " + first.getPosition() + "To" + newPosition + " Button " + e.getModifiersEx() + " " + InputEvent.BUTTON1_DOWN_MASK);
|
|
}
|
|
changed(null,null,map);
|
|
}
|
|
|
|
@Override
|
|
public void mouseMoved(MouseEvent e) {
|
|
// System.out.println("Mouse moved (" + e.getX() + ',' + e.getY() + ')');
|
|
Position newPosition = getPosition(e.getX(),e.getY());
|
|
if ( ( newPosition.getX() >= 0 ) && ( newPosition.getY() >= 0 ) )
|
|
{
|
|
requestFocus();
|
|
}
|
|
}
|
|
|
|
public void setShowAll(boolean showall )
|
|
{
|
|
this.showAll = showall;
|
|
System.out.println(showAll ? "show all" : "X");
|
|
}
|
|
|
|
void checkExit()
|
|
{
|
|
if ((sX == gX) && (sY == gY))
|
|
{
|
|
System.out.println("Exit found by human !");
|
|
}
|
|
}
|
|
|
|
void goNorth()
|
|
{
|
|
resetResolution();
|
|
if ((map.getWalls(sX, sY) & Brick.UP) == 0)
|
|
{
|
|
sY = sY - 1;
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
|
|
checkExit();
|
|
}
|
|
|
|
}
|
|
|
|
void goSouth()
|
|
{
|
|
resetResolution();
|
|
if ((map.getWalls(sX, sY) & Brick.DOWN) == 0)
|
|
{
|
|
sY = sY + 1;
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
|
|
checkExit();
|
|
}
|
|
|
|
}
|
|
|
|
void goEast()
|
|
{
|
|
resetResolution();
|
|
if ((map.getWalls(sX, sY) & Brick.RIGHT) == 0)
|
|
{
|
|
sX = sX + 1;
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
|
|
checkExit();
|
|
}
|
|
}
|
|
|
|
void goWest()
|
|
{
|
|
resetResolution();
|
|
if ((map.getWalls(sX, sY) & Brick.LEFT) == 0)
|
|
{
|
|
sX = sX - 1;
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
|
|
checkExit();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
width, height of one cell,
|
|
offsetX, offsetY of upper left corner
|
|
**/
|
|
// public MazeComponent(WallsProvider map, MazeCellParameters cp)
|
|
public MazeComponent(LabyModel map, MazeCellParameters cp)
|
|
{
|
|
super();
|
|
this.cp = cp;
|
|
this.map = map;
|
|
setPreferredSize(cp.getDimension());
|
|
gX = map.getWidth() - 1;
|
|
gY = map.getHeight() - 1;
|
|
}
|
|
|
|
// public void resetWallsProvider(WallsProvider map)
|
|
public void resetWallsProvider(LabyModel map)
|
|
{
|
|
this.map = map;
|
|
solvedPath=null;
|
|
// could be kept
|
|
drawingPath=null;
|
|
sX=0;
|
|
sY=0;
|
|
cp.resetMazeWidthHeight(map.getWidth(),map.getHeight());
|
|
setPreferredSize(cp.getDimension());
|
|
}
|
|
|
|
public void setWallSize(int size)
|
|
{
|
|
cp.setCellSize((double) size,(double) size);
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
}
|
|
|
|
public int getAutoSize()
|
|
{
|
|
Rectangle r = getBounds();
|
|
cp.adaptTo((int) r.getWidth(),(int) r.getHeight());
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
return 50;
|
|
}
|
|
|
|
public void changed(Position cl, Position cr, WallsProvider map)
|
|
{
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
Object waiter = new Object();
|
|
synchronized (waiter)
|
|
{
|
|
try
|
|
{
|
|
waiter.wait(10);
|
|
}
|
|
catch (InterruptedException e)
|
|
{
|
|
System.err.println("Interrupted !");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void paintComponent(Graphics g)
|
|
{
|
|
super.paintComponent(g);
|
|
int x = 0;
|
|
int y = 0;
|
|
short walls = 0;
|
|
short path = 0;
|
|
|
|
// try to display only visible part...
|
|
// should compute pX, pY, mX, mY based on clip...
|
|
// Shape s=g.getClip();
|
|
Rectangle r = g.getClipBounds();
|
|
|
|
int mX = (int) ((double) r.getWidth() / cp.getWidth());
|
|
int mY = (int) ((double) r.getHeight() / cp.getHeight());
|
|
int pX = (int) ((double) (r.getX() - cp.getOffsetX()) / cp.getWidth());
|
|
int pY = (int) ((double) (r.getY() - cp.getOffsetY()) / cp.getHeight());
|
|
|
|
if (pX < 0)
|
|
pX = 0;
|
|
if (pY < 0)
|
|
pY = 0;
|
|
if (pX >= map.getWidth())
|
|
return;
|
|
if (pY >= map.getHeight())
|
|
return;
|
|
|
|
mX = mX + pX;
|
|
mY = mY + pY;
|
|
|
|
if (mX > map.getWidth())
|
|
{
|
|
mX = map.getWidth();
|
|
}
|
|
if (mY > map.getHeight())
|
|
{
|
|
mY = map.getHeight();
|
|
}
|
|
|
|
if ((sX == gX) && (sY == gY))
|
|
{
|
|
g.setColor(Color.red);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(Color.green);
|
|
cp.drawDot(g, new Position(gX, gY), pX, pY, mX, mY);
|
|
g.setColor(Color.blue);
|
|
}
|
|
cp.drawDot(g, new Position(sX, sY), pX, pY, mX, mY);
|
|
|
|
synchronized (lockChange)
|
|
{
|
|
g.setColor(Color.red);
|
|
if (current != null)
|
|
{
|
|
cp.drawDot(g, current, pX, pY, mX, mY);
|
|
}
|
|
if (solvedPath != null)
|
|
{
|
|
for (DirectionPosition resolved : solvedPath)
|
|
{
|
|
// cp.drawDot(g, resolved.getPosition(), pX, pY, mX, mY);
|
|
cp.drawPath(g,resolved, pX, pY, mX, mY);
|
|
}
|
|
}
|
|
}
|
|
|
|
int aX = pX;
|
|
int aY = pY;
|
|
|
|
g.setColor(Color.black);
|
|
|
|
// draw all walls within clip bounds horiz first then lines
|
|
for (; pY < mY; pY++)
|
|
{
|
|
for (pX = 0; pX < mX; pX++)
|
|
{
|
|
walls = map.getWalls(pX, pY);
|
|
cp.drawWalls(g, pX, pY, walls);
|
|
}
|
|
}
|
|
|
|
if (this.showAll)
|
|
{
|
|
System.out.println("*");
|
|
pX = aX;
|
|
pY = aY;
|
|
|
|
// draw all path within clip bounds horiz first then lines
|
|
for (; pY < mY; pY++)
|
|
{
|
|
for (pX = 0; pX < mX; pX++)
|
|
{
|
|
path = map.getPath(pX,pY);
|
|
if ( ( path & LabyModel.SOLVED ) == LabyModel.SOLVED )
|
|
{
|
|
g.setColor(Color.green);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(Color.blue);
|
|
}
|
|
if ( path != 0 )
|
|
{
|
|
DirectionPosition dp = new DirectionPosition(path,new Position(pX,pY));
|
|
cp.drawPath(g,dp,pX,pY,pX+1,pY+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public boolean notifySearch(DirectionPosition pPosition)
|
|
{
|
|
synchronized (lockChange)
|
|
{
|
|
current = pPosition.getPosition();
|
|
}
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
return true;
|
|
|
|
}
|
|
|
|
public void notifySearchError(String error)
|
|
{
|
|
System.err.println(error);
|
|
}
|
|
|
|
public void notifyCompletion(LinkedList<DirectionPosition> solvedPath)
|
|
{
|
|
LinkedList<DirectionPosition> newPath = new LinkedList<>(solvedPath);
|
|
System.out.println("resolution completed");
|
|
synchronized (lockChange)
|
|
{
|
|
this.solvedPath = newPath;
|
|
}
|
|
// should redraw ...
|
|
invalidate();
|
|
repaint();
|
|
}
|
|
|
|
public void resetResolution()
|
|
{
|
|
solvedPath = null;
|
|
}
|
|
}
|
|
|
|
private static void setupDisplay(LabyModel model, int W, int H, MazeParams params)
|
|
{
|
|
Display display = new Display(model, W,H,params);
|
|
}
|
|
|
|
public static void save(MazeParamsFixed params,LabyModel model)
|
|
{
|
|
File outfile = new File(params.getSaveDir(), params.getName() + ".raw");
|
|
if (!outfile.exists())
|
|
{
|
|
System.out.println("Saving to " + outfile + " ...");
|
|
try
|
|
{
|
|
FileOutputStream out = new FileOutputStream(outfile);
|
|
model.streamOut("raw", out);
|
|
out.flush();
|
|
out.close();
|
|
System.out.println("... Done.");
|
|
}
|
|
catch (IOException io)
|
|
{
|
|
io.printStackTrace(System.err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
System.out.println("" + outfile + " already exists");
|
|
}
|
|
outfile = new File(params.getSaveDir(), params.getName() + ".stl");
|
|
if (!outfile.exists())
|
|
{
|
|
System.out.println("Saving to " + outfile + " ...");
|
|
try
|
|
{
|
|
FileOutputStream out = new FileOutputStream(outfile);
|
|
Wall3d.streamWallsOut(params.getName(), (WallsProvider) model, out);
|
|
out.flush();
|
|
out.close();
|
|
System.out.println("... Done.");
|
|
}
|
|
catch (IOException io)
|
|
{
|
|
io.printStackTrace(System.err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
System.out.println("" + outfile + " already exists");
|
|
}
|
|
|
|
}
|
|
|
|
public static void main(String pArgs[])
|
|
{
|
|
LabyModel model = null;
|
|
int W = 600;
|
|
int H = 400;
|
|
|
|
System.out.println("Default Locale " + Locale.getDefault());
|
|
|
|
if ( (pArgs.length > 0) && (pArgs[0].length() > 0))
|
|
{
|
|
try
|
|
{
|
|
model = new LabyModel("raw", new FileInputStream(pArgs[0]));
|
|
}
|
|
catch (IOException io)
|
|
{
|
|
io.printStackTrace(System.err);
|
|
System.exit(1);
|
|
}
|
|
|
|
setupDisplay(model, W,H,null);
|
|
}
|
|
else
|
|
{
|
|
MazeParamsFixed params = new MazeParamsFixed(new File("lab"),20,20,12);
|
|
model = new LabyModel(params, new java.util.Random());
|
|
|
|
setupDisplay(model,W,H,params);
|
|
|
|
/*
|
|
model.generateWithEntry(0, 0);
|
|
|
|
model.addEntryOrExit(-1, 0);
|
|
model.addEntryOrExit(params.getWidth(), params.getHeight() - 1);
|
|
|
|
System.out.println("Generation completed");
|
|
*/
|
|
/*
|
|
*/
|
|
}
|
|
|
|
}
|
|
}
|