Left: Implement composite structures

This commit is contained in:
Minh Bui
2020-12-08 18:38:32 +07:00
commit ce1a245ca6
45 changed files with 2343 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
package view;
public enum EventName {
CHOOSE_SHAPE{
@Override
public String toString() {
return "CHOOSE SHAPE";
}
},
CHOOSE_PRIMARY_COLOR {
@Override
public String toString() {
return "CHOOSE PRIMARY COLOR";
}
},
CHOOSE_SECONDARY_COLOR {
@Override
public String toString() {
return "CHOOSE SECONDARY COLOR";
}
},
CHOOSE_SHADING_TYPE {
@Override
public String toString() {
return "CHOOSE SHADING TYPE";
}
},
CHOOSE_MOUSE_MODE {
@Override
public String toString() {
return "CHOOSE MOUSE MODE";
}
},
UNDO,
REDO,
COPY,
PASTE,
DELETE,
GROUP,
UNGROUP
}
+62
View File
@@ -0,0 +1,62 @@
package view.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.EnumMap;
import model.AbstractShape;
import model.ShapeColor;
import model.ShapeShadingType;
import view.interfaces.IDrawShape;
import view.interfaces.PaintCanvasBase;
/**
* DrawEllipse class
*
* Responsibility: Contains strategy for drawing an ellipse.
*
* @author Minh Bui
*/
public class DrawEllipse implements IDrawShape {
@Override
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
Stroke selectStroke, boolean offset) {
Graphics2D graphics2d = canvas.getGraphics2D();
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
Stroke customStroke = new BasicStroke(5f);
int width = Math.abs(shape.getEndX() - shape.getStartX());
int height = Math.abs(shape.getEndY() - shape.getStartY());
int x = Math.min(shape.getStartX(), shape.getEndX());
int y = Math.min(shape.getStartY(), shape.getEndY());
if (offset) {
x = x - SELECT_STROKE_OFFSET;
y = y - SELECT_STROKE_OFFSET;
width = width + SELECT_WIDTH_HEIGHT_OFFSET;
height = height + SELECT_WIDTH_HEIGHT_OFFSET;
graphics2d.setStroke(selectStroke);
} else {
graphics2d.setStroke(customStroke);
}
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
graphics2d.fillOval(x, y, width, height);
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
graphics2d.drawOval(x, y, width, height);
} else {
graphics2d.fillOval(x, y, width, height);
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
graphics2d.drawOval(x, y, width, height);
}
}
}
+61
View File
@@ -0,0 +1,61 @@
package view.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.EnumMap;
import model.AbstractShape;
import model.ShapeColor;
import model.ShapeShadingType;
import view.interfaces.IDrawShape;
import view.interfaces.PaintCanvasBase;
/**
* DrawRectangle class
*
* Responsibility: Contains strategy for drawing rectangle.
*
* @author Minh Bui
*/
public class DrawRectangle implements IDrawShape {
@Override
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
Stroke selectStroke, boolean offset) {
Graphics2D graphics2d = canvas.getGraphics2D();
int width = Math.abs(shape.getEndX() - shape.getStartX());
int height = Math.abs(shape.getEndY() - shape.getStartY());
int x = Math.min(shape.getStartX(), shape.getEndX());
int y = Math.min(shape.getStartY(), shape.getEndY());
Stroke customStroke = new BasicStroke(5f);
if (offset) {
x = x - SELECT_STROKE_OFFSET;
y = y - SELECT_STROKE_OFFSET;
width = width + SELECT_WIDTH_HEIGHT_OFFSET;
height = height + SELECT_WIDTH_HEIGHT_OFFSET;
graphics2d.setStroke(selectStroke);
} else {
graphics2d.setStroke(customStroke);
}
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
graphics2d.fillRect(x, y, width, height);
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
graphics2d.drawRect(x, y, width, height);
} else {
graphics2d.fillRect(x, y, width, height);
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
graphics2d.drawRect(x, y, width, height);
}
}
}
+72
View File
@@ -0,0 +1,72 @@
package view.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Stroke;
import java.util.EnumMap;
import model.AbstractShape;
import model.Shape;
import model.ShapeColor;
import model.ShapeShadingType;
import view.interfaces.IDrawShape;
import view.interfaces.PaintCanvasBase;
/**
* DrawTriangle class
*
* Responsibility: Contains strategy for drawing a triangle.
**
* @author Minh Bui
*/
public class DrawTriangle implements IDrawShape {
@Override
public void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap,
Stroke selectStroke, boolean offset) {
Graphics2D graphics2d = canvas.getGraphics2D();
graphics2d.setColor(colorMap.get(shape.getPrimaryColor()));
Stroke customStroke = new BasicStroke(5f);
//int width = Math.abs(shape.getEndX() - shape.getStartX());
int height = Math.abs(shape.getEndY() - shape.getStartY());
int x = Math.min(shape.getStartX(), shape.getEndX());
int y = Math.min(shape.getStartY(), shape.getEndY());
int xMax = Math.max(shape.getStartX(), shape.getEndX());
int yMax = Math.max(shape.getStartY(), shape.getEndY());
int[] xPoints = new int[3];
int[] yPoints = new int[3];
if (!offset) {
xPoints[0] = x; xPoints[1] = xMax; xPoints[2] = x;
yPoints[0] = y; yPoints[1] = yMax; yPoints[2] = y + height;
graphics2d.setStroke(customStroke);
} else {
xPoints[0] = x - SELECT_STROKE_OFFSET;
xPoints[1] = xMax + SELECT_STROKE_OFFSET * 2;
xPoints[2] = x - SELECT_STROKE_OFFSET;
yPoints[0] = y - SELECT_STROKE_OFFSET * 2;
yPoints[1] = yMax + SELECT_STROKE_OFFSET;
yPoints[2] = y - SELECT_STROKE_OFFSET + height + SELECT_WIDTH_HEIGHT_OFFSET;
graphics2d.setStroke(selectStroke);
}
Polygon triangle = new Polygon(xPoints, yPoints, 3);
if (shape.getShadingType() == ShapeShadingType.FILLED_IN) {
graphics2d.fillPolygon(triangle);
} else if (shape.getShadingType() == ShapeShadingType.OUTLINE) {
graphics2d.drawPolygon(triangle);
} else {
graphics2d.fillPolygon(triangle);
graphics2d.setColor(colorMap.get(shape.getSecondaryColor()));
graphics2d.drawPolygon(triangle);
}
}
}
+129
View File
@@ -0,0 +1,129 @@
package view.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
import model.*;
import model.interfaces.IShapeListObserver;
import view.interfaces.IDrawShape;
import view.interfaces.PaintCanvasBase;
/**
* Drawer class
*
* Responsibility: Responsible for selecting specific algorithm to draw shapes and their
* corresponding boundaries from a ShapeList object.
*
* @author Minh Bui
*/
public class Drawer implements IShapeListObserver {
private static Drawer uniqueInstance;
private PaintCanvasBase canvas;
private final EnumMap<ShapeColor, Color> colorMap;
private final EnumMap<ShapeType, IDrawShape> strategies;
private IDrawShape strategy;
private final Stroke selectStroke;
private Drawer(PaintCanvasBase canvas) {
this.canvas = canvas;
colorMap = new EnumMap<>(ShapeColor.class);
colorMap.put(ShapeColor.BLACK, Color.BLACK);
colorMap.put(ShapeColor.BLUE, Color.BLUE);
colorMap.put(ShapeColor.CYAN, Color.CYAN);
colorMap.put(ShapeColor.DARK_GRAY, Color.DARK_GRAY);
colorMap.put(ShapeColor.GRAY, Color.GRAY);
colorMap.put(ShapeColor.GREEN, Color.GREEN);
colorMap.put(ShapeColor.LIGHT_GRAY, Color.LIGHT_GRAY);
colorMap.put(ShapeColor.MAGENTA, Color.MAGENTA);
colorMap.put(ShapeColor.ORANGE, Color.ORANGE);
colorMap.put(ShapeColor.PINK, Color.PINK);
colorMap.put(ShapeColor.RED, Color.RED);
colorMap.put(ShapeColor.WHITE, Color.WHITE);
colorMap.put(ShapeColor.YELLOW, Color.YELLOW);
strategies = new EnumMap<>(ShapeType.class);
strategies.put(ShapeType.RECTANGLE, new DrawRectangle());
strategies.put(ShapeType.ELLIPSE, new DrawEllipse());
strategies.put(ShapeType.TRIANGLE, new DrawTriangle());
strategy = null;
selectStroke = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
1, new float[]{9}, 0);
}
public static Drawer getInstance(PaintCanvasBase canvas) {
if (uniqueInstance == null)
uniqueInstance = new Drawer(canvas);
else
uniqueInstance.canvas = canvas;
return uniqueInstance;
}
@Override
public void update(List<AbstractShape> shapeList) {
// Clear the canvas for redraw
Graphics2D graphics2d = canvas.getGraphics2D();
graphics2d.setColor(canvas.getBackground());
graphics2d.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
Iterator<AbstractShape> shapeListIter = shapeList.iterator();
while (shapeListIter.hasNext()) {
AbstractShape curShape = shapeListIter.next();
//this.draw2(curShape, curShape.isSelected());
this.draw(curShape);
}
}
public void draw2(AbstractShape aShape, boolean isSelected) {
if (aShape != null) {
// Draw the abstract shape first.
strategy = strategies.get(aShape.getShapeType());
if (strategy != null) {
if (!aShape.isBoundary() && !aShape.isComposite())
strategy.draw(aShape, canvas, colorMap, selectStroke, false);
else
if (isSelected && !aShape.isInGroup())
strategy.draw(aShape, canvas, colorMap, selectStroke, true);
}
// Draw its children.
List<AbstractShape> aShapeComponents = aShape.getComponents();
if (aShapeComponents == null) return;
Iterator<AbstractShape> componentsIter = aShapeComponents.iterator();
while(componentsIter.hasNext()) {
AbstractShape component = componentsIter.next();
draw2(component, isSelected);
}
}
}
public void draw(AbstractShape aShape) {
strategy = strategies.get(aShape.getShapeType());
if (strategy != null) {
strategy.draw(aShape, canvas, colorMap, selectStroke, false);
if (aShape instanceof Shape) {
Shape shape = (Shape) aShape;
if (shape.isInGroup() && shape.isSelected()) {
strategy = strategies.get(ShapeType.RECTANGLE);
strategy.draw(shape.getGroupBoundary(), canvas, colorMap, selectStroke, true);
} else if (shape.isSelected()) {
strategy.draw(shape.getBoundary(), canvas, colorMap, selectStroke, true);
}
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
package view.gui;
import javax.swing.*;
import view.EventName;
import view.interfaces.IDialogChoice;
import view.interfaces.IEventCallback;
import view.interfaces.IGuiWindow;
import view.interfaces.IUiModule;
public class Gui implements IUiModule {
private final IGuiWindow GUI;
public Gui(IGuiWindow gui) {
this.GUI = gui;
}
@Override
public void addEvent(EventName eventName, IEventCallback callback) {
JButton button = GUI.getButton(eventName);
button.addActionListener((ActionEvent) -> callback.run());
}
@Override
public <T> T getDialogResponse(IDialogChoice dialogSettings) {
Object selectedValue = JOptionPane.showInputDialog(null,
dialogSettings.getDialogText(),
dialogSettings.getDialogTitle(),
JOptionPane.PLAIN_MESSAGE,
null,
dialogSettings.getDialogOptions(),
dialogSettings.getCurrentSelection());
return selectedValue == null
? (T)dialogSettings.getCurrentSelection()
: (T)selectedValue;
}
}
+96
View File
@@ -0,0 +1,96 @@
package view.gui;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.swing.*;
import javax.swing.border.*;
import view.interfaces.IGuiWindow;
import view.EventName;
import java.awt.*;
public class GuiWindow extends JFrame implements IGuiWindow {
private final int DEFAULT_WIDTH = 1250;
private final int DEFAULT_HEIGHT = 800;
private final String DEFAULT_TITLE = "JPaint";
private final Insets defaultButtonDimensions
= new Insets(5, 8, 5, 8);
private final Map<EventName, JButton> eventButtons = new HashMap<>();
public GuiWindow(JComponent canvas){
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(DEFAULT_TITLE);
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
setBackground(new Color(155, 155, 155));
setExtendedState(JFrame.MAXIMIZED_BOTH);
JPanel window = createWindow();
window.add(canvas, BorderLayout.CENTER);
validate();
}
@Override
public JButton getButton(EventName eventName) {
if(!eventButtons.containsKey(eventName))
throw new NoSuchElementException("No button exists for action " + eventName.toString());
return eventButtons.get(eventName);
}
private JPanel createWindow() {
JPanel contentPane = createBackgroundPanel();
JPanel buttonPanel = createMenu();
contentPane.add(buttonPanel, BorderLayout.NORTH);
return contentPane;
}
private JPanel createMenu() {
JPanel buttonPanel = createButtonPanel();
for(EventName eventName : EventName.values()){
addButtonToPanel(eventName, buttonPanel);
}
return buttonPanel;
}
private void addButtonToPanel(EventName eventName, JPanel panel) {
JButton newButton = createButton(eventName);
eventButtons.put(eventName, newButton);
panel.add(newButton);
}
private JButton createButton(EventName eventName) {
JButton newButton = new JButton(eventName.toString());
newButton.setForeground(Color.BLACK);
newButton.setBackground(Color.WHITE);
newButton.setBorder(createButtonBorder());
return newButton;
}
private Border createButtonBorder() {
Border line = new LineBorder(Color.BLACK);
Border margin = new EmptyBorder(defaultButtonDimensions);
return new CompoundBorder(line, margin);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
flowLayout.setAlignment(FlowLayout.LEFT);
panel.setBackground(Color.lightGray);
return panel;
}
private JPanel createBackgroundPanel() {
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(0, 0, 0, 0));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.setBackground(Color.WHITE);
setContentPane(contentPane);
return contentPane;
}
}
+13
View File
@@ -0,0 +1,13 @@
package view.gui;
import view.interfaces.PaintCanvasBase;
import javax.swing.JComponent;
import java.awt.*;
public class PaintCanvas extends PaintCanvasBase {
public Graphics2D getGraphics2D() {
return (Graphics2D)getGraphics();
}
}
+11
View File
@@ -0,0 +1,11 @@
package view.interfaces;
public interface IDialogChoice<T> {
String getDialogTitle();
String getDialogText();
T[] getDialogOptions();
T getCurrentSelection();
}
+27
View File
@@ -0,0 +1,27 @@
package view.interfaces;
import java.awt.Color;
import java.awt.Stroke;
import java.util.EnumMap;
import model.AbstractShape;
import model.ShapeColor;
/**
* IDrawShape interface
*
* Responsibility: Interface for the strategy pattern for drawing shapes.
*
* Note that if offset is true, an algorithm for drawing boundary is used instead.
*
* @author Minh Bui
*
*/
public interface IDrawShape {
int SELECT_STROKE_OFFSET = 5;
int SELECT_WIDTH_HEIGHT_OFFSET = 10;
void draw(AbstractShape shape, PaintCanvasBase canvas, EnumMap<ShapeColor, Color> colorMap, Stroke selectStroke,
boolean offset);
}
+5
View File
@@ -0,0 +1,5 @@
package view.interfaces;
public interface IEventCallback {
void run();
}
+9
View File
@@ -0,0 +1,9 @@
package view.interfaces;
import view.EventName;
import javax.swing.*;
public interface IGuiWindow {
JButton getButton(EventName eventName);
}
+8
View File
@@ -0,0 +1,8 @@
package view.interfaces;
import view.EventName;
public interface IUiModule {
void addEvent(EventName eventName, IEventCallback command);
<T> T getDialogResponse(IDialogChoice dialogChoice);
}
+8
View File
@@ -0,0 +1,8 @@
package view.interfaces;
import javax.swing.*;
import java.awt.*;
public abstract class PaintCanvasBase extends JComponent {
public abstract Graphics2D getGraphics2D();
}