Graphic Rendering System 1.0
A Java-based graphic rendering system implementing various design patterns
Loading...
Searching...
No Matches
SwingGraphicsApp.java
Go to the documentation of this file.
1package com.example.graphics;
2
3import com.example.graphics.factory.ShapeFactory;
4import com.example.graphics.factory.SwingRendererFactory;
5import com.example.graphics.model.Line;
6import com.example.graphics.model.Shape;
7import com.example.graphics.observer.ConsoleLogger;
8import com.example.graphics.render.Renderer;
9import com.example.graphics.render.SwingRenderer;
10import com.example.graphics.singleton.RenderingConfig;
11import com.example.graphics.util.FileManager;
12import com.example.graphics.visitor.JsonExportVisitor;
13import com.example.graphics.visitor.XmlExportVisitor;
14import com.example.graphics.proxy.RemoteRendererProxy;
15
16import javax.swing.*;
17import javax.swing.filechooser.FileNameExtensionFilter;
18import java.awt.*;
19import java.awt.event.*;
20import java.io.File;
21import java.io.FileWriter;
22import java.io.IOException;
23import java.util.List;
24
28public class SwingGraphicsApp extends JFrame {
29 private final Drawing drawing;
33 private final FileManager fileManager;
34
36 private String currentShapeType = "Circle";
37 private Point dragStart;
38 private boolean isDragging = false;
39 private boolean isDrawingLine = false;
40 private boolean isDrawingTriangle = false;
41 private int triangleStage = 0;
42 private int x1, y1, x2, y2; // 用于存储线条和三角形的点
43
44 private JLabel statusLabel;
45 private JToggleButton remoteRenderingToggle;
46 private boolean isRemoteRenderingEnabled = false;
47
49 super("图形渲染系统");
50
51 // 获取配置
53
54 // 创建工厂和渲染器
55 SwingRendererFactory rendererFactory = new SwingRendererFactory();
56 localRenderer = (SwingRenderer) rendererFactory.createRenderer(800, 600);
57
58 // 创建远程渲染器代理
60 remoteRenderer.setLocalRenderer(localRenderer); // 设置本地渲染器引用
61
62 // 创建绘图对象,默认使用本地渲染器
64 drawing.addObserver(new ConsoleLogger());
65
66 // 创建形状工厂
68
69 // 创建文件管理器
71
72 // 设置UI
73 setupUI();
74
75 // 设置窗口属性
76 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
77 setSize(1000, 700);
78 setLocationRelativeTo(null);
79 setVisible(true);
80 }
81
82 private void setupUI() {
83 JPanel mainPanel = new JPanel(new BorderLayout());
84
85 // 创建菜单栏
86 JMenuBar menuBar = createMenuBar();
87 setJMenuBar(menuBar);
88
89 // 创建工具栏
90 JToolBar toolBar = createToolBar();
91 mainPanel.add(toolBar, BorderLayout.NORTH);
92
93 // 创建渲染面板
94 JPanel renderPanel = localRenderer.getRenderPanel();
95 renderPanel.setBorder(BorderFactory.createLoweredBevelBorder());
96 setupRenderPanelListeners(renderPanel);
97
98 // 创建滚动面板
99 JScrollPane scrollPane = new JScrollPane(renderPanel);
100 mainPanel.add(scrollPane, BorderLayout.CENTER);
101
102 // 创建状态栏
103 JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT));
104 statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
105 statusLabel = new JLabel("就绪");
106 statusBar.add(statusLabel);
107 mainPanel.add(statusBar, BorderLayout.SOUTH);
108
109 setContentPane(mainPanel);
110
111 // 添加一些初始形状作为示例
113 }
114
115 private JMenuBar createMenuBar() {
116 JMenuBar menuBar = new JMenuBar();
117
118 // 文件菜单
119 JMenu fileMenu = new JMenu("文件");
120
121 JMenuItem newItem = new JMenuItem("新建");
122 newItem.addActionListener(e -> newDrawing());
123 fileMenu.add(newItem);
124
125 JMenuItem openItem = new JMenuItem("打开...");
126 openItem.addActionListener(e -> openDrawing());
127 fileMenu.add(openItem);
128
129 fileMenu.addSeparator();
130
131 JMenuItem saveItem = new JMenuItem("保存...");
132 saveItem.addActionListener(e -> saveDrawing());
133 fileMenu.add(saveItem);
134
135 JMenuItem saveJsonItem = new JMenuItem("导出为JSON...");
136 saveJsonItem.addActionListener(e -> exportToJson());
137 fileMenu.add(saveJsonItem);
138
139 JMenuItem saveXmlItem = new JMenuItem("导出为XML...");
140 saveXmlItem.addActionListener(e -> exportToXml());
141 fileMenu.add(saveXmlItem);
142
143 fileMenu.addSeparator();
144
145 JMenuItem exitItem = new JMenuItem("退出");
146 exitItem.addActionListener(e -> System.exit(0));
147 fileMenu.add(exitItem);
148
149 menuBar.add(fileMenu);
150
151 // 编辑菜单
152 JMenu editMenu = new JMenu("编辑");
153
154 JMenuItem undoItem = new JMenuItem("撤销");
155 undoItem.addActionListener(e -> {
156 if (drawing.undo()) {
157 drawing.render();
158 statusLabel.setText("已撤销操作");
159 } else {
160 statusLabel.setText("没有操作可撤销");
161 }
162 });
163 editMenu.add(undoItem);
164
165 JMenuItem redoItem = new JMenuItem("重做");
166 redoItem.addActionListener(e -> {
167 if (drawing.redo()) {
168 drawing.render();
169 statusLabel.setText("已重做操作");
170 } else {
171 statusLabel.setText("没有操作可重做");
172 }
173 });
174 editMenu.add(redoItem);
175
176 editMenu.addSeparator();
177
178 JMenuItem clearItem = new JMenuItem("清除所有");
179 clearItem.addActionListener(e -> {
180 clearDrawing();
181 });
182 editMenu.add(clearItem);
183
184 menuBar.add(editMenu);
185
186 // 渲染菜单
187 JMenu renderMenu = new JMenu("渲染");
188
189 JMenuItem localRenderItem = new JMenuItem("本地渲染");
190 localRenderItem.addActionListener(e -> switchToLocalRendering());
191 renderMenu.add(localRenderItem);
192
193 JMenuItem remoteRenderItem = new JMenuItem("远程渲染");
194 remoteRenderItem.addActionListener(e -> switchToRemoteRendering());
195 renderMenu.add(remoteRenderItem);
196
197 menuBar.add(renderMenu);
198
199 // 帮助菜单
200 JMenu helpMenu = new JMenu("帮助");
201
202 JMenuItem aboutItem = new JMenuItem("关于");
203 aboutItem.addActionListener(e -> {
204 JOptionPane.showMessageDialog(this,
205 "图形渲染系统\n版本 1.0\n\n使用Java Swing开发的图形渲染应用\n支持多种设计模式",
206 "关于图形渲染系统\nsxizhuo.cn\nmengxizhuo@gmail.com",
207 JOptionPane.INFORMATION_MESSAGE);
208 });
209 helpMenu.add(aboutItem);
210
211 menuBar.add(helpMenu);
212
213 return menuBar;
214 }
215
216 private void addSampleShapes() {
217 // 添加一个圆形
218 Shape circle = shapeFactory.createCircle(200, 150, 50);
219 drawing.addShape(circle);
220
221 // 添加一个矩形
222 Shape rectangle = shapeFactory.createRectangle(400, 200, 150, 100);
223 drawing.addShape(rectangle);
224
225 // 添加一条线
226 Shape line = shapeFactory.createLine(100, 300, 500, 400);
227 drawing.addShape(line);
228
229 // 添加一个三角形
230 Shape triangle = shapeFactory.createTriangle(300, 100, 350, 200, 250, 200);
231 drawing.addShape(triangle);
232
233 // 刷新显示
234 drawing.render();
235
236 statusLabel.setText("已添加示例图形");
237 }
238
239 private JToolBar createToolBar() {
240 JToolBar toolBar = new JToolBar();
241 toolBar.setFloatable(false);
242
243 // 形状选择按钮
244 String[] shapeTypes = {"Circle", "Rectangle", "Line", "Triangle"};
245 ButtonGroup shapeGroup = new ButtonGroup();
246
247 for (String type : shapeTypes) {
248 JToggleButton button = new JToggleButton(type);
249 button.addActionListener(e -> {
250 currentShapeType = type;
251 statusLabel.setText("已选择: " + type);
252
253 // 重置绘制状态
254 isDrawingLine = false;
255 isDrawingTriangle = false;
256 triangleStage = 0;
257 });
258 shapeGroup.add(button);
259 toolBar.add(button);
260
261 // 默认选择圆形
262 if ("Circle".equals(type)) {
263 button.setSelected(true);
264 }
265 }
266
267 toolBar.addSeparator();
268
269 // 清除按钮
270 JButton clearButton = new JButton("清除");
271 clearButton.addActionListener(e -> clearDrawing());
272 toolBar.add(clearButton);
273
274 toolBar.addSeparator();
275
276 // 渲染模式切换按钮
277 remoteRenderingToggle = new JToggleButton("远程渲染");
278 remoteRenderingToggle.addActionListener(e -> {
279 if (remoteRenderingToggle.isSelected()) {
281 } else {
283 }
284 });
285 toolBar.add(remoteRenderingToggle);
286
287 return toolBar;
288 }
289
290 // 切换到本地渲染
291 private void switchToLocalRendering() {
293 // 如果当前是远程渲染,则断开连接
294 remoteRenderer.disconnect();
295
296 // 切换到本地渲染器
297 drawing.setRenderer(localRenderer);
298
299 // 更新状态
301 remoteRenderingToggle.setSelected(false);
302 statusLabel.setText("【本地渲染模式】已切换到本地渲染");
303
304 // 重新渲染
305 drawing.render();
306 }
307 }
308
309 // 切换到远程渲染
310 private void switchToRemoteRendering() {
312 // 尝试连接到远程渲染器
313 boolean connected = remoteRenderer.connect();
314
315 if (connected) {
316 // 切换到远程渲染器
317 drawing.setRenderer(remoteRenderer);
318
319 // 更新状态
321 remoteRenderingToggle.setSelected(true);
322 statusLabel.setText("【远程渲染模式】已连接到远程渲染服务");
323
324 // 重新渲染
325 drawing.render();
326 } else {
327 // 连接失败,保持本地渲染
328 remoteRenderingToggle.setSelected(false);
329 statusLabel.setText("远程渲染连接失败,保持本地渲染模式");
330 JOptionPane.showMessageDialog(this,
331 "无法连接到远程渲染服务",
332 "连接错误",
333 JOptionPane.ERROR_MESSAGE);
334 }
335 }
336 }
337
338 private void setupRenderPanelListeners(JPanel renderPanel) {
339 renderPanel.addMouseListener(new MouseAdapter() {
340 @Override
341 public void mousePressed(MouseEvent e) {
342 dragStart = e.getPoint();
343
344 // 检查是否点击了现有形状
345 for (Shape shape : drawing.getShapes()) {
346 if (isPointOnShape(shape, e.getPoint())) {
347 selectedShape = shape;
348 isDragging = true;
349 statusLabel.setText("已选中图形: " + getShapeTypeName(shape));
350 return;
351 }
352 }
353
354 // 如果没有点击现有形状,处理新形状创建
355 if (currentShapeType.equals("Line")) {
356 if (!isDrawingLine) {
357 // 开始绘制线条
358 isDrawingLine = true;
359 x1 = e.getX();
360 y1 = e.getY();
361 statusLabel.setText("绘制线条: 已设置起点,请点击终点");
362 } else {
363 // 完成线条绘制
364 x2 = e.getX();
365 y2 = e.getY();
366 Shape line = shapeFactory.createLine(x1, y1, x2, y2);
367 drawing.addShape(line);
368 drawing.render();
369 isDrawingLine = false;
370 statusLabel.setText("已添加线条");
371 }
372 } else if (currentShapeType.equals("Triangle")) {
373 if (triangleStage == 0) {
374 // 第一个点
375 x1 = e.getX();
376 y1 = e.getY();
377 triangleStage = 1;
378 isDrawingTriangle = true;
379 statusLabel.setText("绘制三角形: 已设置第一个顶点,请点击第二个顶点");
380 } else if (triangleStage == 1) {
381 // 第二个点
382 x2 = e.getX();
383 y2 = e.getY();
384 triangleStage = 2;
385 statusLabel.setText("绘制三角形: 已设置第二个顶点,请点击第三个顶点");
386 } else if (triangleStage == 2) {
387 // 第三个点,完成三角形
388 Shape triangle = shapeFactory.createTriangle(x1, y1, x2, y2, e.getX(), e.getY());
389 drawing.addShape(triangle);
390 drawing.render();
391 triangleStage = 0;
392 isDrawingTriangle = false;
393 statusLabel.setText("已添加三角形");
394 }
395 } else {
396 // 创建其他形状
397 createNewShape(e.getPoint());
398 }
399 }
400
401 @Override
402 public void mouseReleased(MouseEvent e) {
403 isDragging = false;
404 if (selectedShape != null) {
405 statusLabel.setText("已移动图形: " + getShapeTypeName(selectedShape));
406 }
407 selectedShape = null;
408 }
409 });
410
411 renderPanel.addMouseMotionListener(new MouseMotionAdapter() {
412 @Override
413 public void mouseDragged(MouseEvent e) {
414 if (isDragging && selectedShape != null) {
415 drawing.moveShape(selectedShape, e.getX(), e.getY());
416 statusLabel.setText("正在移动图形: " + getShapeTypeName(selectedShape));
417 }
418 }
419
420 @Override
421 public void mouseMoved(MouseEvent e) {
422 // 显示鼠标坐标
423 statusLabel.setText("坐标: (" + e.getX() + ", " + e.getY() + ")");
424 }
425 });
426 }
427
428 private String getShapeTypeName(Shape shape) {
429 if (shape instanceof com.example.graphics.model.Circle) {
430 return "圆形";
431 } else if (shape instanceof com.example.graphics.model.Rectangle) {
432 return "矩形";
433 } else if (shape instanceof com.example.graphics.model.Line) {
434 return "直线";
435 } else if (shape instanceof com.example.graphics.model.Triangle) {
436 return "三角形";
437 }
438 return "图形";
439 }
440
441 private boolean isPointOnShape(Shape shape, Point point) {
442 if (shape instanceof com.example.graphics.model.Circle) {
443 com.example.graphics.model.Circle circle = (com.example.graphics.model.Circle) shape;
444 double distance = Math.sqrt(
445 Math.pow(point.x - circle.getX(), 2) +
446 Math.pow(point.y - circle.getY(), 2)
447 );
448 return distance <= circle.getRadius();
449 } else if (shape instanceof com.example.graphics.model.Rectangle) {
450 com.example.graphics.model.Rectangle rect = (com.example.graphics.model.Rectangle) shape;
451 return point.x >= rect.getX() && point.x <= rect.getX() + rect.getWidth() &&
452 point.y >= rect.getY() && point.y <= rect.getY() + rect.getHeight();
453 } else if (shape instanceof com.example.graphics.model.Line) {
454 com.example.graphics.model.Line line = (com.example.graphics.model.Line) shape;
455 double lineLength = Math.sqrt(
456 Math.pow(line.getX2() - line.getX1(), 2) +
457 Math.pow(line.getY2() - line.getY1(), 2)
458 );
459
460 double d1 = Math.sqrt(
461 Math.pow(point.x - line.getX1(), 2) +
462 Math.pow(point.y - line.getY1(), 2)
463 );
464
465 double d2 = Math.sqrt(
466 Math.pow(point.x - line.getX2(), 2) +
467 Math.pow(point.y - line.getY2(), 2)
468 );
469
470 // 允许5个像素的误差
471 return Math.abs(d1 + d2 - lineLength) <= 5;
472 } else if (shape instanceof com.example.graphics.model.Triangle) {
473 com.example.graphics.model.Triangle triangle = (com.example.graphics.model.Triangle) shape;
474
475 // 使用重心坐标法判断点是否在三角形内
476 int x1 = triangle.getX1();
477 int y1 = triangle.getY1();
478 int x2 = triangle.getX2();
479 int y2 = triangle.getY2();
480 int x3 = triangle.getX3();
481 int y3 = triangle.getY3();
482
483 double denominator = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
484 double a = ((y2 - y3) * (point.x - x3) + (x3 - x2) * (point.y - y3)) / denominator;
485 double b = ((y3 - y1) * (point.x - x3) + (x1 - x3) * (point.y - y3)) / denominator;
486 double c = 1 - a - b;
487
488 // 如果重心坐标都在0到1之间,则点在三角形内
489 return a >= 0 && a <= 1 && b >= 0 && b <= 1 && c >= 0 && c <= 1;
490 }
491 return false;
492 }
493
494 private void createNewShape(Point point) {
495 Shape newShape = null;
496
497 switch (currentShapeType) {
498 case "Circle":
499 newShape = shapeFactory.createCircle(point.x, point.y, 50);
500 statusLabel.setText("已添加圆形");
501 break;
502 case "Rectangle":
503 newShape = shapeFactory.createRectangle(point.x, point.y, 100, 80);
504 statusLabel.setText("已添加矩形");
505 break;
506 // Line和Triangle在mousePressed中处理
507 }
508
509 if (newShape != null) {
510 drawing.addShape(newShape);
511 drawing.render();
512 }
513 }
514
515 private void clearDrawing() {
516 // 清除所有形状
517 for (Shape shape : drawing.getShapes().toArray(new Shape[0])) {
518 drawing.removeShape(shape);
519 }
520 drawing.render();
521 statusLabel.setText("已清除所有图形");
522 }
523
524 private void newDrawing() {
525 if (!drawing.getShapes().isEmpty()) {
526 int option = JOptionPane.showConfirmDialog(this,
527 "是否保存当前图形?",
528 "新建",
529 JOptionPane.YES_NO_CANCEL_OPTION);
530
531 if (option == JOptionPane.YES_OPTION) {
532 saveDrawing();
533 } else if (option == JOptionPane.CANCEL_OPTION) {
534 return;
535 }
536 }
537
538 clearDrawing();
539 }
540
541 private void openDrawing() {
542 JFileChooser fileChooser = new JFileChooser();
543 fileChooser.setDialogTitle("打开图形文件");
544
545 // 添加文件过滤器
546 FileNameExtensionFilter binFilter = new FileNameExtensionFilter("二进制文件 (*.bin)", "bin");
547 FileNameExtensionFilter jsonFilter = new FileNameExtensionFilter("JSON文件 (*.json)", "json");
548 fileChooser.addChoosableFileFilter(binFilter);
549 fileChooser.addChoosableFileFilter(jsonFilter);
550 fileChooser.setFileFilter(binFilter); // 默认选择二进制文件
551
552 int result = fileChooser.showOpenDialog(this);
553
554 if (result == JFileChooser.APPROVE_OPTION) {
555 File file = fileChooser.getSelectedFile();
556 String filePath = file.getAbsolutePath();
557
558 try {
559 List<Shape> shapes;
560
561 if (filePath.toLowerCase().endsWith(".json")) {
562 shapes = fileManager.loadDrawingJson(filePath);
563 } else {
564 // 默认为二进制文件
565 shapes = fileManager.loadDrawingBinary(filePath);
566 }
567
568 // 清除当前图形
569 clearDrawing();
570
571 // 添加加载的图形
572 for (Shape shape : shapes) {
573 drawing.addShape(shape);
574 }
575
576 drawing.render();
577 statusLabel.setText("已加载图形文件: " + file.getName());
578
579 } catch (Exception e) {
580 JOptionPane.showMessageDialog(this,
581 "加载文件失败: " + e.getMessage(),
582 "错误",
583 JOptionPane.ERROR_MESSAGE);
584 statusLabel.setText("加载文件失败");
585 }
586 }
587 }
588
589 private void saveDrawing() {
590 JFileChooser fileChooser = new JFileChooser();
591 fileChooser.setDialogTitle("保存图形文件");
592
593 // 添加文件过滤器
594 FileNameExtensionFilter binFilter = new FileNameExtensionFilter("二进制文件 (*.bin)", "bin");
595 fileChooser.addChoosableFileFilter(binFilter);
596 fileChooser.setFileFilter(binFilter);
597
598 int result = fileChooser.showSaveDialog(this);
599
600 if (result == JFileChooser.APPROVE_OPTION) {
601 File file = fileChooser.getSelectedFile();
602 String filePath = file.getAbsolutePath();
603
604 // 确保文件有正确的扩展名
605 if (!filePath.toLowerCase().endsWith(".bin")) {
606 filePath += ".bin";
607 }
608
609 try {
610 fileManager.saveDrawingBinary(drawing, filePath);
611 statusLabel.setText("已保存图形文件: " + new File(filePath).getName());
612 } catch (IOException e) {
613 JOptionPane.showMessageDialog(this,
614 "保存文件失败: " + e.getMessage(),
615 "错误",
616 JOptionPane.ERROR_MESSAGE);
617 statusLabel.setText("保存文件失败");
618 }
619 }
620 }
621
622 private void exportToJson() {
624 drawing.acceptVisitor(visitor);
625 saveToFile(visitor.getJsonOutput(), "json");
626 }
627
628 private void exportToXml() {
630 drawing.acceptVisitor(visitor);
631 saveToFile(visitor.getXmlOutput(), "xml");
632 }
633
634 private void saveToFile(String content, String extension) {
635 JFileChooser fileChooser = new JFileChooser();
636 fileChooser.setDialogTitle("保存为" + extension.toUpperCase() + "文件");
637
638 int userSelection = fileChooser.showSaveDialog(this);
639
640 if (userSelection == JFileChooser.APPROVE_OPTION) {
641 File fileToSave = fileChooser.getSelectedFile();
642 String filePath = fileToSave.getAbsolutePath();
643
644 // 确保文件有正确的扩展名
645 if (!filePath.toLowerCase().endsWith("." + extension)) {
646 filePath += "." + extension;
647 }
648
649 try {
650 if (extension.equals("json")) {
651 fileManager.saveDrawingJson(content, filePath);
652 } else if (extension.equals("xml")) {
653 fileManager.saveDrawingXml(content, filePath);
654 } else {
655 try (FileWriter writer = new FileWriter(filePath)) {
656 writer.write(content);
657 }
658 }
659
660 JOptionPane.showMessageDialog(this,
661 "文件已保存至:" + filePath,
662 "保存成功", JOptionPane.INFORMATION_MESSAGE);
663 statusLabel.setText("已导出为" + extension.toUpperCase() + "文件");
664 } catch (IOException e) {
665 JOptionPane.showMessageDialog(this,
666 "保存文件失败:" + e.getMessage(),
667 "错误", JOptionPane.ERROR_MESSAGE);
668 statusLabel.setText("导出失败");
669 }
670 }
671 }
672
673 public static void main(String[] args) {
674 // 使用Swing线程
675 SwingUtilities.invokeLater(SwingGraphicsApp::new);
676 }
677}
void setupRenderPanelListeners(JPanel renderPanel)
boolean isPointOnShape(Shape shape, Point point)
void saveToFile(String content, String extension)
final RemoteRendererProxy remoteRenderer
static synchronized RenderingConfig getInstance()