主程序
以上所有的铺垫工作都已经完毕,接下来主程序的绘制异常简单,如果有不明白的地方可以在返回之前的示例中查看。实际上,最后的测试阶段会发现Bug大都出现在前面的铺垫中,主程序只是将它们做一个有机的相加的过程。下面我将对主程序的每一个部分做详细解释。
字段声明
- 主程序中的字段一览表
|修饰符|类型|名称|说明|
|—|---|—|---|
|protected|ArrayList< ImageElement>|elements|存储图形元素的列表|
|protected|HashMap< String, Strategy>|strategy|存储所有策略的字典|
|protected|Strategy|currentStrategy|当前所采用的的绘图策略|
|protected|Color|currentColor|当前的绘图颜色|
|protected|boolean|similar|是否开启快捷选择操作|
|protected|JButton|backgroundButton|修改画板背景色的按钮|
|protected|JButton|colorButton|修改当前绘图颜色的按钮|
|protected|JButton|similarButton|开启/关闭快捷选择操作的按钮|
|protected|BufferedImage|lastImage|打开的以保存图像|
构造函数
由于主程序继承自SwingFramework类,所以构造函数只需要对一些继承自父类的元素稍作修改即可。
- 主程序构造函数源代码
public Editor() {
appBorder = new Color(0xFFEBCD);
appBackground = Color.WHITE;
appFont = new Font("Courier New", Font.PLAIN, 20);
appWidth = 1080;
appHeight = 720;
appWorldWidth = 16.0f;
appWorldHeight = 9.0f;
appSleep = 10L;
appMaintainRatio = true;
appBorderScale = 0.95f;
appTitle = "Editor";
currentColor = Color.BLACK;
similar = false;
}
GUI设计
在父类中提供了onCreateAndShowGUI方法供子类继承来设计程序界面,这是一个很长的方法,但是很好理解。它在主面板里增加了菜单栏和工具栏,在菜单栏中增加新建,保存,打开,关闭,说明,关于等功能,在工具栏中为每一个策略和一些其他小功能增添了按钮,在改变策略时改变currentStrategy字段即可。
- onCreateAndShowGUI方法源代码
@Override
protected void onCreateAndShowGUI() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
JMenuItem item = new JMenuItem(new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
if (saved()||empty()) {
onNew();
} else {
int isSave = JOptionPane.showConfirmDialog(Editor.this, "是否保存文件?",
"保存", JOptionPane.YES_NO_OPTION);
if (isSave == JOptionPane.NO_OPTION) {
onNew();
} else {
save();
}
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Open") {
@Override
public void actionPerformed(ActionEvent e) {
if (saved()||empty()) {
onOpen();
} else {
int isSave = JOptionPane.showConfirmDialog(Editor.this, "是否保存文件?",
"保存", JOptionPane.YES_NO_OPTION);
if (isSave == JOptionPane.NO_OPTION) {
onOpen();
} else {
save();
}
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Save") {
@Override
public void actionPerformed(ActionEvent e) {
while (!saved()) {
save();
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Exit") {
@Override
public void actionPerformed(ActionEvent e) {
Editor.this.dispatchEvent(new WindowEvent(
Editor.this, WindowEvent.WINDOW_CLOSING
));
}
});
menu.add(item);
menuBar.add(menu);
menu = new JMenu("Help");
item = new JMenuItem(new AbstractAction("Instruction") {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
Editor.this, "Instruction of this app!!!",
"Instruction", JOptionPane.INFORMATION_MESSAGE
);
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("About") {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
Editor.this, "About this app!!!",
"About", JOptionPane.INFORMATION_MESSAGE
);
}
});
menu.add(item);
menuBar.add(menu);
setJMenuBar(menuBar);
JToolBar bar = new JToolBar();
bar.setFloatable(false);
backgroundButton = new JButton("■");
backgroundButton.setForeground(appBackground);
backgroundButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JColorChooser chooser=new JColorChooser();
Color color = chooser.showDialog(
Editor.this, "选取颜色", Color.BLACK);
if (color != null) {
appBackground = color;
}
backgroundButton.setForeground(appBackground);
canvas.setBackground(appBackground);
}
});
bar.add(backgroundButton);
colorButton = new JButton("■");
colorButton.setForeground(currentColor);
colorButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JColorChooser chooser=new JColorChooser();
Color color = chooser.showDialog(
Editor.this, "选取颜色", Color.BLACK);
if (color != null) {
currentColor = color;
}
colorButton.setForeground(currentColor);
currentStrategy.setColor(currentColor);
}
});
bar.add(colorButton);
JButton b = new JButton("•");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("points strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("-");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("line strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("△");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("triangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("□");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("rectangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("○");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("circle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("O");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("oval strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("◆");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill polygon strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("▲");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill triangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("■");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill rectangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("●");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill circle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("Θ");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill oval strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("abc");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("string strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
similarButton = new JButton("×");
similarButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
similar = !similar;
if (similar) {
similarButton.setForeground(Color.RED);
} else {
similarButton.setForeground(Color.BLACK);
}
}
});
bar.add(similarButton);
b = new JButton("⊙");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("null strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
getMainPanel().add(bar, BorderLayout.NORTH);
}
菜单操作方法
在onCreateAndShowGUI方法的菜单操作中只是调用了执行操作的方法,而没有对具体的文件操作做具体实现,下面是对这些方法的详解。
- 菜单操作方法一览表
修饰符 | 返回值 | 函数名 | 参数 | 说明 |
---|---|---|---|---|
protected | boolean | empty | () | 判断当前画板是否为空 |
protected | boolean | saved | () | 判断当前图像是否保存 |
protected | void | save | () | 保存当前绘制图像 |
protected | void | onNew | () | 新建画板并还原默认设置 |
protected | void | onOpen | () | 新建画板并打开指定图像 |
- 菜单操作方法源代码
protected boolean empty() {
if (elements.size() == 0) {
return true;
} else {
for (ImageElement element : elements) {
if (!element.empty()) {
return false;
}
}
return true;
}
}
protected boolean saved() {
if (empty()) {
return true;
}else {
return elements.get(elements.size() - 1) instanceof SaveImageElement;
}
}
protected void save() {
BufferedImage image = new BufferedImage(getScreenWidth(), getScreenHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
Matrix3x3f view = getViewportTransform();
g2d.setColor(appBackground);
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
if (lastImage != null) {
g2d.drawImage(lastImage, 0, 0, image.getWidth(), image.getHeight(), null);
}
for (ImageElement element : elements) {
element.draw(g2d, view);
}
g2d.dispose();
String fileName = (String) JOptionPane.showInputDialog(Editor.this, "文件名:",
"保存", JOptionPane.PLAIN_MESSAGE, null,null,"new");
if (fileName == null) {
return;
}
fileName = fileName + ".jpg";
File file = new File(fileName);
try {
if (!ImageIO.write(image, "jpg", file)) {
throw new IOException("No 'jpg' image writer found");
}
} catch (IOException e) {
e.printStackTrace();
}finally {
elements.add(new SaveImageElement());
}
}
protected void onNew() {
elements.clear();
currentColor = Color.BLACK;
colorButton.setForeground(currentColor);
currentStrategy.finishDrawing();
currentStrategy = strategy.get("null strategy");
currentStrategy.setColor(currentColor);
}
protected void onOpen() {
onNew();
String fileName = JOptionPane.showInputDialog(Editor.this, "文件名:",
"打开", JOptionPane.PLAIN_MESSAGE);
if (fileName == null) {
return;
}
if (fileName.endsWith(".jpg") || fileName.endsWith(".png")) {
try {
lastImage = ImageIO.read(new File(fileName));
} catch (IOException e1) {
JOptionPane.showMessageDialog(
Editor.this, "No such image exists!!!",
"Warning", JOptionPane.INFORMATION_MESSAGE);
e1.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(
Editor.this, "Can't solve image type!!!",
"Warning", JOptionPane.INFORMATION_MESSAGE);
}
}
初始化操作
初始化操作调用继承自Framework类的initialize方法,其主要作用为向策略字典中添加所有的绘制策略,是本程序中最难理解的一个函数。
每个BeginEnd策略的初始化框架如下:
strategy.put("strategy name",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
//some thing to draw
}
@Override
public Vector2f similar(Vector2f mousePos) {
//some thing for similar
}
});
}
});
在字典中加入匿名的策略,需要重写抽象策略的addElement方法,而addElement方法又需要一个匿名的图形元素,其中重写了draw方法和similar方法。实际上就是一种不同的draw方法和similar方法,决定了一个图形元素,同时也决定了一种绘图策略。
- initialize方法源代码
@Override
protected void initialize() {
super.initialize();
elements = new ArrayList<ImageElement>();
strategy = new HashMap<>();
strategy.put("null strategy",
new NullStrategy(this, keyboard, mouse, elements));
strategy.put("points strategy",
new PointsStrategy(this, keyboard, mouse, elements));
strategy.put("line strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawPolygon(g2d, new Vector2f[]{begin, end}, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
}else if(center.similar(mousePos,EPSILON)){
return center;
} else {
return null;
}
}
});
}
});
strategy.put("triangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawTriangle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f tmp = end.sub(begin);
Vector2f topTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() + Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
Vector2f bottomTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() - Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if (topTmp.similar(mousePos,EPSILON)) {
return topTmp;
} else if (bottomTmp.similar(mousePos,EPSILON)) {
return bottomTmp;
}else {
return null;
}
}
});
}
});
strategy.put("rectangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawRect(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f other = begin.add(new Vector2f(end.sub(begin).x, 0));
Vector2f another = end.sub(new Vector2f(end.sub(begin).x, 0));
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if(other.similar(mousePos,EPSILON)){
return other;
} else if (another.similar(mousePos, EPSILON)) {
return another;
} else if (center.similar(mousePos, EPSILON)) {
return center;
} else {
return null;
}
}
});
}
});
strategy.put("circle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawCircle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f circle = mousePos.sub(begin).norm();
circle = begin.add(circle.mul(end.sub(begin).len()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("oval strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawOval(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
Vector2f circle = mousePos.sub(center);
float length = Math.abs(end.x - begin.x);
float width = Math.abs(end.y - begin.y);
float radian = circle.angle();
circle = center.add(
new Vector2f(
(float) (length / 2 * Math.cos(radian)),
(float) (width / 2 * Math.sin(radian))));
if (center.similar(mousePos,EPSILON)) {
return center;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill triangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillTriangle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f tmp = end.sub(begin);
Vector2f topTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() + Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
Vector2f bottomTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() - Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if (topTmp.similar(mousePos,EPSILON)) {
return topTmp;
} else if (bottomTmp.similar(mousePos,EPSILON)) {
return bottomTmp;
}else {
return null;
}
}
});
}
});
strategy.put("fill rectangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillRect(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f other = begin.add(new Vector2f(end.sub(begin).x, 0));
Vector2f another = end.sub(new Vector2f(end.sub(begin).x, 0));
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if(other.similar(mousePos,EPSILON)){
return other;
} else if (another.similar(mousePos, EPSILON)) {
return another;
} else if (center.similar(mousePos, EPSILON)) {
return center;
} else {
return null;
}
}
});
}
});
strategy.put("fill circle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillCircle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f circle = mousePos.sub(begin).norm();
circle = begin.add(circle.mul(end.sub(begin).len()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill oval strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillOval(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
Vector2f circle = mousePos.sub(center);
float length = Math.abs(end.x - begin.x);
float width = Math.abs(end.y - begin.y);
float radian = circle.angle();
circle = center.add(
new Vector2f(
(float) (length / 2 * Math.cos(radian)),
(float) (width / 2 * Math.sin(radian))));
if (center.similar(mousePos,EPSILON)) {
return center;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill polygon strategy",
new FillPolygonStrategy(this, keyboard, mouse, elements));
strategy.put("string strategy",
new StringStrategy(this, keyboard, mouse, elements));
currentStrategy = strategy.get("null strategy");
}
主程序输入处理
由于主程序继承自SwingFramework类,那么就需要实现Framework类里的processInput方法来处理输入。因为之前已经把所有的输入处理都交给了不同的策略,所以这一步非常的简单,整个方法只有以下两行。
@Override
protected void processInput(float delta) {
super.processInput(delta);
currentStrategy.processInput();
}
主程序渲染处理
由于主程序继承自SwingFramework类,那么就需要实现Framework类里的render方法来处理渲染。这里将渲染处理分为3部分,**打开的图像文件的渲染,图形元素列表的渲染,和快捷选择提示圆的渲染。**这一步也非常简单,因为具体的实现方法之前已经定义好了,所以这里只需要调用每一个图形元素的draw方法即可。具体代码如下。
protected void renderLastImage(Graphics g) {
if (lastImage != null) {
g.drawImage(lastImage, 0, 0, getScreenWidth(), getScreenHeight(),null);
}
}
@Override
protected void render(Graphics g) {
super.render(g);
renderLastImage(g);
Matrix3x3f view = getViewportTransform();
ArrayList<ImageElement> elementsTmp = new ArrayList<>(elements);
for (ImageElement element : elementsTmp) {
element.draw((Graphics2D)g, view);
}
if (similar) {
renderSimilar(g);
}
}
protected void renderSimilar(Graphics g) {
Matrix3x3f view = getViewportTransform();
Vector2f mousePos = getWorldMousePosition();
ArrayList<ImageElement> elementsTmp = new ArrayList<>(elements);
for (ImageElement element : elementsTmp) {
if (element instanceof Similarable) {
Vector2f tmpPos = ((Similarable) element).similar(mousePos);
if (tmpPos != null && tmpPos.sub(mousePos).len() > Vector2f.EPSILON) {
Utility.fillSimilarCircle((Graphics2D) g, tmpPos, view);
if (mouse.buttonDownOnce(MouseEvent.BUTTON2)) {
currentStrategy.similar(tmpPos, getViewportTransform());
}
}
}
}
}
主程序主函数
其实到这里,整个程序已经结束了,但是要有主函数,程序才能运行。其实应该可以想到,非常简单,主函数只有一行。
public static void main(String[] args) {
launchApp(new Editor());
}
launchApp即可,Now,You can enjoy it!
附录:主程序源代码
import Rendering.element.BeginEndImageElement;
import Rendering.element.ImageElement;
import Rendering.element.SaveImageElement;
import Rendering.element.Similarable;
import Rendering.strategy.*;
import Rendering.utils.Matrix3x3f;
import Rendering.utils.SwingFramework;
import Rendering.utils.Utility;
import Rendering.utils.Vector2f;
import Rendering.strategy.FillPolygonStrategy;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
public class Editor extends SwingFramework {
protected ArrayList<ImageElement> elements;
protected HashMap<String, Strategy> strategy;
protected Strategy currentStrategy;
protected Color currentColor;
protected boolean similar;
protected JButton backgroundButton;
protected JButton colorButton;
protected JButton similarButton;
protected BufferedImage lastImage;
public Editor() {
appBorder = new Color(0xFFEBCD);
appBackground = Color.WHITE;
appFont = new Font("Courier New", Font.PLAIN, 20);
appWidth = 1080;
appHeight = 720;
appWorldWidth = 16.0f;
appWorldHeight = 9.0f;
appSleep = 10L;
appMaintainRatio = true;
appBorderScale = 0.95f;
appTitle = "Editor";
currentColor = Color.BLACK;
similar = false;
}
@Override
protected void onCreateAndShowGUI() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
JMenuItem item = new JMenuItem(new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
if (saved()) {
onNew();
} else {
int isSave = JOptionPane.showConfirmDialog(Editor.this, "是否保存文件?",
"保存", JOptionPane.YES_NO_OPTION);
if (isSave == JOptionPane.NO_OPTION) {
onNew();
} else {
save();
}
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Open") {
@Override
public void actionPerformed(ActionEvent e) {
if (saved()) {
onOpen();
} else {
int isSave = JOptionPane.showConfirmDialog(Editor.this, "是否保存文件?",
"保存", JOptionPane.YES_NO_OPTION);
if (isSave == JOptionPane.NO_OPTION) {
onOpen();
} else {
save();
}
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Save") {
@Override
public void actionPerformed(ActionEvent e) {
while (!saved()) {
save();
}
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("Exit") {
@Override
public void actionPerformed(ActionEvent e) {
Editor.this.dispatchEvent(new WindowEvent(
Editor.this, WindowEvent.WINDOW_CLOSING
));
}
});
menu.add(item);
menuBar.add(menu);
menu = new JMenu("Help");
item = new JMenuItem(new AbstractAction("Instruction") {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
Editor.this, "Instruction of this app!!!",
"Instruction", JOptionPane.INFORMATION_MESSAGE
);
}
});
menu.add(item);
item = new JMenuItem(new AbstractAction("About") {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
Editor.this, "About this app!!!",
"About", JOptionPane.INFORMATION_MESSAGE
);
}
});
menu.add(item);
menuBar.add(menu);
setJMenuBar(menuBar);
JToolBar bar = new JToolBar();
bar.setFloatable(false);
backgroundButton = new JButton("■");
backgroundButton.setForeground(appBackground);
backgroundButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JColorChooser chooser=new JColorChooser();
Color color = chooser.showDialog(
Editor.this, "选取颜色", Color.BLACK);
if (color != null) {
appBackground = color;
}
backgroundButton.setForeground(appBackground);
canvas.setBackground(appBackground);
}
});
bar.add(backgroundButton);
colorButton = new JButton("■");
colorButton.setForeground(currentColor);
colorButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JColorChooser chooser=new JColorChooser();
Color color = chooser.showDialog(
Editor.this, "选取颜色", Color.BLACK);
if (color != null) {
currentColor = color;
}
colorButton.setForeground(currentColor);
currentStrategy.setColor(currentColor);
}
});
bar.add(colorButton);
JButton b = new JButton("•");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("points strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("-");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("line strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("△");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("triangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("□");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("rectangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("○");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("circle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("O");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("oval strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("◆");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill polygon strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("▲");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill triangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("■");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill rectangle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("●");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill circle strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("Θ");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("fill oval strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
b = new JButton("abc");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("string strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
similarButton = new JButton("×");
similarButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
similar = !similar;
if (similar) {
similarButton.setForeground(Color.RED);
} else {
similarButton.setForeground(Color.BLACK);
}
}
});
bar.add(similarButton);
b = new JButton("⊙");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentStrategy.finishDrawing();
currentStrategy = strategy.get("null strategy");
currentStrategy.setColor(currentColor);
}
});
bar.add(b);
getMainPanel().add(bar, BorderLayout.NORTH);
}
protected boolean empty() {
if (elements.size() == 0) {
return true;
} else {
for (ImageElement element : elements) {
if (!element.empty()) {
return false;
}
}
return true;
}
}
protected boolean saved() {
if (empty()) {
return true;
}else {
return elements.get(elements.size() - 1) instanceof SaveImageElement;
}
}
protected void save() {
BufferedImage image = new BufferedImage(getScreenWidth(), getScreenHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
Matrix3x3f view = getViewportTransform();
g2d.setColor(appBackground);
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
if (lastImage != null) {
g2d.drawImage(lastImage, 0, 0, image.getWidth(), image.getHeight(), null);
}
for (ImageElement element : elements) {
element.draw(g2d, view);
}
g2d.dispose();
String fileName = (String) JOptionPane.showInputDialog(Editor.this, "文件名:",
"保存", JOptionPane.PLAIN_MESSAGE, null,null,"new");
if (fileName == null) {
return;
}
fileName = fileName + ".jpg";
File file = new File(fileName);
try {
if (!ImageIO.write(image, "jpg", file)) {
throw new IOException("No 'jpg' image writer found");
}
} catch (IOException e) {
e.printStackTrace();
}finally {
elements.add(new SaveImageElement());
}
}
protected void onNew() {
elements.clear();
currentColor = Color.BLACK;
colorButton.setForeground(currentColor);
currentStrategy.finishDrawing();
currentStrategy = strategy.get("null strategy");
currentStrategy.setColor(currentColor);
}
protected void onOpen() {
onNew();
String fileName = JOptionPane.showInputDialog(Editor.this, "文件名:",
"打开", JOptionPane.PLAIN_MESSAGE);
if (fileName == null) {
return;
}
if (fileName.endsWith(".jpg") || fileName.endsWith(".png")) {
try {
lastImage = ImageIO.read(new File(fileName));
} catch (IOException e1) {
JOptionPane.showMessageDialog(
Editor.this, "No such image exists!!!",
"Warning", JOptionPane.INFORMATION_MESSAGE);
e1.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(
Editor.this, "Can't solve image type!!!",
"Warning", JOptionPane.INFORMATION_MESSAGE);
}
}
@Override
protected void initialize() {
super.initialize();
elements = new ArrayList<ImageElement>();
strategy = new HashMap<>();
strategy.put("null strategy",
new NullStrategy(this, keyboard, mouse, elements));
strategy.put("points strategy",
new PointsStrategy(this, keyboard, mouse, elements));
strategy.put("line strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawPolygon(g2d, new Vector2f[]{begin, end}, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
}else if(center.similar(mousePos,EPSILON)){
return center;
} else {
return null;
}
}
});
}
});
strategy.put("triangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawTriangle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f tmp = end.sub(begin);
Vector2f topTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() + Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
Vector2f bottomTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() - Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if (topTmp.similar(mousePos,EPSILON)) {
return topTmp;
} else if (bottomTmp.similar(mousePos,EPSILON)) {
return bottomTmp;
}else {
return null;
}
}
});
}
});
strategy.put("rectangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawRect(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f other = begin.add(new Vector2f(end.sub(begin).x, 0));
Vector2f another = end.sub(new Vector2f(end.sub(begin).x, 0));
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if(other.similar(mousePos,EPSILON)){
return other;
} else if (another.similar(mousePos, EPSILON)) {
return another;
} else if (center.similar(mousePos, EPSILON)) {
return center;
} else {
return null;
}
}
});
}
});
strategy.put("circle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawCircle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f circle = mousePos.sub(begin).norm();
circle = begin.add(circle.mul(end.sub(begin).len()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("oval strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.drawOval(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
Vector2f circle = mousePos.sub(center);
float length = Math.abs(end.x - begin.x);
float width = Math.abs(end.y - begin.y);
float radian = circle.angle();
circle = center.add(
new Vector2f(
(float) (length / 2 * Math.cos(radian)),
(float) (width / 2 * Math.sin(radian))));
if (center.similar(mousePos,EPSILON)) {
return center;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill triangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillTriangle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f tmp = end.sub(begin);
Vector2f topTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() + Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
Vector2f bottomTmp = begin.add(
Vector2f.polar(
new Double(tmp.angle() - Math.PI / 6.0).floatValue(),
new Double(tmp.len() * Math.sqrt(3.0)).floatValue()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if (topTmp.similar(mousePos,EPSILON)) {
return topTmp;
} else if (bottomTmp.similar(mousePos,EPSILON)) {
return bottomTmp;
}else {
return null;
}
}
});
}
});
strategy.put("fill rectangle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillRect(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f other = begin.add(new Vector2f(end.sub(begin).x, 0));
Vector2f another = end.sub(new Vector2f(end.sub(begin).x, 0));
Vector2f center = begin.add(end.sub(begin).div(2));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (end.similar(mousePos,EPSILON)) {
return end;
} else if(other.similar(mousePos,EPSILON)){
return other;
} else if (another.similar(mousePos, EPSILON)) {
return another;
} else if (center.similar(mousePos, EPSILON)) {
return center;
} else {
return null;
}
}
});
}
});
strategy.put("fill circle strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillCircle(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f circle = mousePos.sub(begin).norm();
circle = begin.add(circle.mul(end.sub(begin).len()));
if (begin.similar(mousePos,EPSILON)) {
return begin;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill oval strategy",
new BeginEndStrategy(this, keyboard, mouse, elements) {
@Override
protected void addElement() {
elements.add(new BeginEndImageElement(begin, end, color) {
@Override
public void draw(Graphics2D g2d, Matrix3x3f view) {
Utility.fillOval(g2d, begin, end, view, color);
}
@Override
public Vector2f similar(Vector2f mousePos) {
Vector2f center = begin.add(end.sub(begin).div(2));
Vector2f circle = mousePos.sub(center);
float length = Math.abs(end.x - begin.x);
float width = Math.abs(end.y - begin.y);
float radian = circle.angle();
circle = center.add(
new Vector2f(
(float) (length / 2 * Math.cos(radian)),
(float) (width / 2 * Math.sin(radian))));
if (center.similar(mousePos,EPSILON)) {
return center;
} else if (circle.similar(mousePos, EPSILON)) {
return circle;
} else {
return null;
}
}
});
}
});
strategy.put("fill polygon strategy",
new FillPolygonStrategy(this, keyboard, mouse, elements));
strategy.put("string strategy",
new StringStrategy(this, keyboard, mouse, elements));
currentStrategy = strategy.get("null strategy");
}
@Override
protected void processInput(float delta) {
super.processInput(delta);
currentStrategy.processInput();
}
protected void renderLastImage(Graphics g) {
if (lastImage != null) {
g.drawImage(lastImage, 0, 0, getScreenWidth(), getScreenHeight(),null);
}
}
@Override
protected void render(Graphics g) {
super.render(g);
renderLastImage(g);
Matrix3x3f view = getViewportTransform();
ArrayList<ImageElement> elementsTmp = new ArrayList<>(elements);
for (ImageElement element : elementsTmp) {
element.draw((Graphics2D)g, view);
}
if (similar) {
renderSimilar(g);
}
}
protected void renderSimilar(Graphics g) {
Matrix3x3f view = getViewportTransform();
Vector2f mousePos = getWorldMousePosition();
ArrayList<ImageElement> elementsTmp = new ArrayList<>(elements);
for (ImageElement element : elementsTmp) {
if (element instanceof Similarable) {
Vector2f tmpPos = ((Similarable) element).similar(mousePos);
if (tmpPos != null && tmpPos.sub(mousePos).len() > Vector2f.EPSILON) {
Utility.fillSimilarCircle((Graphics2D) g, tmpPos, view);
if (mouse.buttonDownOnce(MouseEvent.BUTTON2)) {
currentStrategy.similar(tmpPos, getViewportTransform());
}
}
}
}
}
public static void main(String[] args) {
launchApp(new Editor());
}
}
更多: