A Little Polymorphic Fun
Download these two files; they are the latest versions.
Marketing has called! They want circles, and your quarterly bonus is riding on it. What are we to do? We really want that Tahitian vacation!
So, let's make a Circle class. Just copy the Square class and make a simple modifiction in the draw method.
import javafx.scene.paint.Color;
import javafx.scene.canvas.GraphicsContext;
public class Circle
{
private final Color color;
private final double xCenter;
private final double yCenter;
private final double size;
public Circle(Color color, double xCenter, double yCenter, double size)
{
this.color = color;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.size = size;
}
public void draw(GraphicsContext pen)
{
//draw myself
pen.setFill(color);
pen.fillOval(xCenter - size/2, yCenter - size/2, size, size);
}
}
Houston, we have a problem. Our Coordianted class only uses Squares. What do we do? First of all, we glue the Square and Circle classes together by creating an interface.
import javafx.scene.canvas.GraphicsContext;
public interface Drawable
{
public void draw(GraphicsContext g);
}
Now accept the contract.
import javafx.scene.paint.Color;
import javafx.scene.canvas.GraphicsContext;
public class Square implements Drawable
{
private final Color color;
private final double xCenter;
private final double yCenter;
private final double size;
public Square(Color color, double xCenter, double yCenter, double size)
{
this.color = color;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.size = size;
}
public void draw(GraphicsContext pen)
{
//draw myself
pen.setFill(color);
pen.fillRect(xCenter - size/2, yCenter - size/2, size, size);
}
}
import javafx.scene.paint.Color;
import javafx.scene.canvas.GraphicsContext;
public class Circle implements Drawable
{
private final Color color;
private final double xCenter;
private final double yCenter;
private final double size;
public Circle(Color color, double xCenter, double yCenter, double size)
{
this.color = color;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.size = size;
}
public void draw(GraphicsContext pen)
{
//draw myself
pen.setFill(color);
pen.fillOval(xCenter - size/2, yCenter - size/2, size, size);
}
}
Now we are in better "shape."
In our Coordinated class, we have an array list of squares. We must
modify it to be an array list of Drawables. We also changed the
name of squares
to shapes
.
/**************************************************
* Author: Morrison
* Date: 26 Oct 202020
**************************************************/
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.MenuBar;
import javafx.scene.control.ColorPicker;
import javafx.scene.paint.Color;
import java.util.ArrayList;
public class Coordinated extends Application
{
private Stage primary;
private Canvas canvas;
private GraphicsContext pen;
private Color currentColor;
private Color bgColor;
private ArrayList<Drawable> shapes;
private ArrayList<Drawable> redoBoneyard;
public Coordinated()
{
canvas = new Canvas(800,600);
pen = canvas.getGraphicsContext2D();
currentColor = Color.BLACK;
shapes = new ArrayList<>();
redoBoneyard = new ArrayList<>();
bgColor = Color.WHITE;
}
@Override
public void init()
{
}
@Override
public void start(Stage primary)
{
this.primary = primary;
BorderPane bp = new BorderPane();
bp.setCenter(canvas);
refresh();
bp.setTop(buildMenus());
canvas.setOnMouseClicked( e ->
{
s = new Square(currentColor, e.getX(), e.getY(), 100);
shapes.add(s);
s.draw(pen);
});
primary.setScene(new Scene(bp));
primary.show();
}
private MenuBar buildMenus()
{
MenuBar mbar = new MenuBar();
Menu colorMenu = new Menu("Color");
Menu backgroundMenu = new Menu("Background");
Menu undoRedoMenu = new Menu("Undo/Redo");
MenuItem undoItem = new MenuItem("Undo");
MenuItem redoItem = new MenuItem("Redo");
undoRedoMenu.getItems().addAll(undoItem, redoItem);
undoItem.setOnAction( e ->
{
if(!shapes.isEmpty())
{
redoBoneyard.add(shapes.remove(shapes.size() - 1));
refresh();
}
});
redoItem.setOnAction( e ->
{
if(!redoBoneyard.isEmpty())
{
shapes.add(redoBoneyard.remove(redoBoneyard.size() - 1));
refresh();
}
});
mbar.getMenus().addAll(undoRedoMenu, backgroundMenu, colorMenu,
shapeMenu);
colorMenu.getItems().addAll(
new ColorMenuItem(Color.RED, "red"),
new ColorMenuItem(Color.BLUE, "blue"),
new ColorMenuItem(Color.GREEN, "green"),
new ColorMenuItem(Color.ORANGE, "orange"),
new ColorMenuItem(Color.rgb(0, 0x1a, 0x57), "dook")
);
MenuItem customColor = new MenuItem("custom...");
colorMenu.getItems().add(customColor);
customColor.setOnAction( choose ->
{
Stage popup = new Stage();
Pane p = new Pane();
popup.setScene(new Scene(p));
ColorPicker cp = new ColorPicker();
p.getChildren().add(cp);
popup.show();
cp.setOnAction( e ->
{
currentColor = cp.getValue();
popup.close();
});
});
backgroundMenu.getItems().addAll(
new BgMenuItem(Color.PINK, "pink"),
new BgMenuItem(Color.LIGHTBLUE, "light blue"),
new BgMenuItem(Color.WHITE, "white"),
new BgMenuItem(Color.BLACK, "black")
);
return mbar;
}
public void refresh()
{
pen.setFill(bgColor);
pen.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for(Drawable s: shapes)
{
s.draw(pen);
}
}
@Override
public void stop()
{
}
public static void main(String[] args)
{
Application.launch();
}
/********************** Aliens *************************/
class ColorMenuItem extends MenuItem
{
private final Color color;
public ColorMenuItem(Color color, String name)
{
super(name);
this.color = color;
setOnAction( e ->
{
currentColor = color;
});
}
}
class BgMenuItem extends MenuItem
{
private final Color color;
public BgMenuItem(Color color, String name)
{
super(name);
this.color = color;
setOnAction( e ->
{
bgColor = color;
refresh();
});
}
}
}
Inside of the "Aliens" section, we are going to add a new
item, an enum
enum ShapeType{SQUARE, CIRCLE};