Id like multicolored changing text, I made a list with all the colors
I have 5 g.drawString(); functions running, each of them should be the next color in the list (one above each other.)
private Color[] colors = new Color[12];
Then in my constructor:
colors[0] = new Color(255, 0, 0);
colors[1] = new Color(255, 127, 0);
colors[2] = new Color(255, 255, 0);
colors[3] = new Color(127, 255, 0);
colors[4] = new Color(0, 255, 0);
colors[5] = new Color(0, 255, 127);
colors[6] = new Color(0, 255, 255);
colors[7] = new Color(0, 127, 255);
colors[8] = new Color(0, 0, 255);
colors[9] = new Color(127, 0, 255);
colors[10] = new Color(255, 0, 255);
colors[11] = new Color(255, 0, 127);
How would I make each each letter a different color?
Set The Color: g.setColor(Color object);
Example: g.setColor(colors[5]);
Write Text: g.drawString(String, x, y);
Example: g.drawString("S", 200, 300);
So, Id like S to be the color, colors[0], I made a table below:
Starting | First | Second | Fifth
S -- 0 11 10 7
N -- 1 0 11 8
A -- 2 1 0 9
K -- 3 2 1 10
E -- 4 3 2 11
So it would loop around though each color:
I tried making a function for this, I deleted the code because I'm an idiot -_-
In my main class, I have a game loop that calls the tick and render methods, tick first then render.
I have an enum called STATE which contains menu and game, then the variable gameState of the type state is set to STATE.menu
public enum state {
Menu,
Game,
}
public state gameState = state.Menu;
When gameState is equal to STATE.menu it will call menu.render(g ( <-- The variable im using for Graphics));
Each class has its own render and tick method.
-Tick method, for setting variables etc, if statements, yada yada yada
-Render method, anything to do with drawing pixels
Because the tick method is called every 0.0000000000000000001 seconds, the color changes every 9 millionth of a second and it looks very derpy.
So ill need a timer of some sorts that I can configure with a variable
I want each of the letters to be a different color, one after another in the list
you can refer to the table above if you don't understand but as an example,
the letter a should be colors[0]
and then b, colors[1]
and c, colors[2]
the colors should alter,
so a would be colors[2]
so b would be colors[0]
so c would be colors[1]
I've probably been unclear on 1001 things, so please shout at me in the comments ^-^
Thanks for reading!
If I understood correctly, there are mainly two issues:
Painting the letters in different colors, cycling through the given array
Updating the colors (but at a fixed time, not with every "tick")
Cycling through the colors can be achieved by introducing a "colorOffset". You can add this color offset to the index that you use to access the color in the array, and take this modulo the array length to obtain a valid array index:
int colorOffset = ... // Counted up or down all the time
// The index of the color for the i'th letter
int colorIndex = (i+colorOffset)%colors.length;
if (colorIndex < 0) colorIndex += colors.length;
g.setColor(colors[colorIndex]);
The second part, regarding the update: I assume that you have a game loop that is run in an own thread. Then, in thisTickMethodThatYouHaveBeenTalkingAbout, you can check the current system time with System.nanoTime(), and compute the time that has passed since the last update. If the time is larger than the desired interval, you perform an update, by increasing the colorOffset and triggering a repaint() (if necessary - you might cover this already with your render() method).
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MulticolorTextAnimation
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MulticolorTextAnimationPanel m = new MulticolorTextAnimationPanel();
f.getContentPane().add(m);
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
while (true)
{
m.thisTickMethodThatYouHaveBeenTalkingAbout();
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
}
});
thread.setDaemon(true);
thread.start();
f.setSize(500,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class MulticolorTextAnimationPanel extends JPanel
{
private String string;
private Color colors[];
private int colorOffset = 0;
private long lastUpdateNs = -1;
private final long updateIntervalMs = 250;
public MulticolorTextAnimationPanel()
{
setFont(new Font("Dialog", Font.BOLD, 45));
string = "I am a string!";
colors = new Color[12];
colors[0] = new Color(255, 0, 0);
colors[1] = new Color(255, 127, 0);
colors[2] = new Color(255, 255, 0);
colors[3] = new Color(127, 255, 0);
colors[4] = new Color(0, 255, 0);
colors[5] = new Color(0, 255, 127);
colors[6] = new Color(0, 255, 255);
colors[7] = new Color(0, 127, 255);
colors[8] = new Color(0, 0, 255);
colors[9] = new Color(127, 0, 255);
colors[10] = new Color(255, 0, 255);
colors[11] = new Color(255, 0, 127);
}
public void thisTickMethodThatYouHaveBeenTalkingAbout()
{
long ns = System.nanoTime();
if (lastUpdateNs < 0)
{
lastUpdateNs = ns;
}
long passedNs = (ns - lastUpdateNs);
long passedMs = passedNs / 1000000;
if (passedMs > updateIntervalMs)
{
// Increase or decrease the color offset,
// depending on whether the colors should
// cycle forward or backward
colorOffset--;
repaint();
lastUpdateNs = ns;
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
FontMetrics fontMetrics = g.getFontMetrics();
int x = 100;
int y = 100;
for (int i=0; i<string.length(); i++)
{
char c = string.charAt(i);
int colorIndex = (i+colorOffset)%colors.length;
if (colorIndex < 0)
{
colorIndex += colors.length;
}
g.setColor(colors[colorIndex]);
g.drawString(String.valueOf(c), x, y);
x += fontMetrics.charWidth(c);
}
}
}
Related
Tell me how to make it change to the next one when you click on a picture. I use a library "ControlP5, cp5.addbutton" with a regular button in which I could do without a picture.
I have two examples, with a regular button and a picture, where I use a different way to change the picture by hovering the mouse and clicking in order to show you clearly.
"minutesss1" - a regular button with public void minutesss1() which uses .setCaptionLabel("ВЫКЛ").
"minutesss2" - How I think to use for the picture .setCaptionLabel("ВЫКЛ") / Not required ?????
Complete example code:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700,400);
PFont fontn = createFont("Times New Roman",18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman",18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200,200)
.setSize(99,25);
PImage[] imgs1 = {loadImage("0.png"),loadImage("1.png"),loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400,200)
.setImages(imgs1);
minutess2.setSize(99,25);
textFont(fontn);}
public void minutesss1() {
min+=10;
if (min>60) {min=0; minutess1.setCaptionLabel("ВЫКЛ"); }
else minutess1.setCaptionLabel(str(min)+" Мин");}
void draw(){
background(0);
fill(255);}
It's great that you've posted the code formatted like that.
It would be even better if you format it in Processing (Ctrl + T) before to make it easier to read.
It is difficult to fully understand what your asking.
If you're using a translation tool you can try breaking long phrases into smaller, simpler sentences. Hopefully the translation tool will do a better job.
From what I can understand there are two questions here:
How can you call the same minutes updating function that works for the first button from the second button ?
How can you use custom images to skin the second button ?
The first question can be tackled multiple ways.
Here are a couple of options:
Option 1: Use controlEvent which gets called automatically when updating any controlP5 component. You can check which button was pressed and call a function accordingly:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700, 400);
PFont fontn = createFont("Times New Roman", 18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman", 18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200, 200)
.setSize(99, 25);
//PImage[] imgs1 = {loadImage("0.png"), loadImage("1.png"), loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
PImage[] imgs1 = {getImage(99,25,color(0,0,192)),
getImage(99,25,color(0,0,240)),
getImage(99,25,color(0,0,120))};
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400, 200)
.setImages(imgs1);
minutess2.setSize(99, 25);
textFont(fontn);
}
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
public void minutesss1() {
min+=10;
if (min>60) {
min=0;
minutess1.setCaptionLabel("ВЫКЛ");
} else minutess1.setCaptionLabel(str(min)+" Мин");
println(min,minutess1.getCaptionLabel().getText());
}
void draw() {
background(0);
fill(255);
}
public void controlEvent(ControlEvent event) {
if(event.controller() == minutess2){
minutesss1();
}
}
Option 2: Extract the instructions of the first button press function into a separate function that can be called by both. This is probably simpler and more intuitive to read:
import controlP5.*;
ControlP5 cp5;
int min=0;
Button minutess1;
Button minutess2;
void setup() {
size(700, 400);
PFont fontn = createFont("Times New Roman", 18);
cp5 = new ControlP5(this);
PFont p = createFont("Times New Roman", 18);
ControlFont font=new
ControlFont(p);
cp5.setFont(font);
minutess1 = cp5.addButton("minutesss1")
.setCaptionLabel("ВЫКЛ")
.setPosition(200, 200)
.setSize(99, 25);
//PImage[] imgs1 = {loadImage("0.png"), loadImage("1.png"), loadImage("2.png")}; // ,loadImage("3.png"),loadImage("4.png"),loadImage("5.png"),loadImage("6.png")
// don't have images to reproduce: making new ones
PImage[] imgs1 = {getImage(99,25,color(0,0,192)),
getImage(99,25,color(0,0,240)),
getImage(99,25,color(0,0,120))};
minutess2 = cp5.addButton("minutesss2")
//.setCaptionLabel("ВЫКЛ")
.setPosition(400, 200)
.setImages(imgs1);
minutess2.setSize(99, 25);
textFont(fontn);
}
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
void updateMinutes(){
min+=10;
if (min>60) {
min=0;
minutess1.setCaptionLabel("ВЫКЛ");
} else minutess1.setCaptionLabel(str(min)+" Мин");
println(min,minutess1.getCaptionLabel().getText());
}
public void minutesss1() {
updateMinutes();
}
public void minutesss2() {
updateMinutes();
}
void draw() {
background(0);
fill(255);
}
Regarding the second part of your question it's unclear if you want to have images just for the default controlP5 states(default, over, active, highlight) using setImages() or not. If you pass more than 4 or less than 3 images they will be ignored as you can see in the source code
If you want to display a different image for each minutes update (e.g. off, 10 , 20, 30, 40, 50, 60) then you will need to make your own custom button.
The logic isn't that complicated and you can use the Button Example as a simpler starting point.
It would be great to encapsulate that more complex custom functionality and you'd need a few Object Oriented Programming (OOP) basics for that. You can check out the Objects tutorial and example
For illustration purposes here's a separate sketch that would display a different image for each of the button press states (ignoring the over/highlight states):
ImageButton button;
void setup(){
size(300, 300);
// button dimensions
int w = 75;
int h = 25;
// test with generated images
button = new ImageButton(112, 137, w, h,
new PImage[]{
// use loadImage with your own images instead of getImage :)
getImage(w, h, color(192, 0, 32 * 2)), // off
getImage(w, h, color(0, 0, 32 * 3)), // 10
getImage(w, h, color(0, 0, 32 * 4)), // 20
getImage(w, h, color(0, 0, 32 * 5)), // 30
getImage(w, h, color(0, 0, 32 * 6)), // 40
getImage(w, h, color(0, 0, 32 * 7)), // 50
getImage(w, h, color(0, 0, 32 * 8)), // 60
});
// loading images will be something similar to:
//button = new ImageButton(112, 137, w, h,
// new PImage[]{
// loadImage("0.png"), // off
// loadImage("1.png"), // 10
// loadImage("2.png"), // 20
// loadImage("3.png"), // 30
// loadImage("4.png"), // 40
// loadImage("5.png"), // 50
// loadImage("6.png"), // 60
// });
}
void draw(){
background(0);
button.draw();
}
void mousePressed(){
button.mousePressed(mouseX,mouseY);
println(button.min);
}
// test images to represent loaded state images
PImage getImage(int w, int h, int c){
PImage img = createImage(w, h, RGB);
java.util.Arrays.fill(img.pixels, c);
img.updatePixels();
return img;
}
// make a custom image button class
class ImageButton{
// minutes is the data it stores
int min = 0;
// images for each state
PImage[] stateImages;
// which image to display
int stateIndex;
// position
int x, y;
// dimensions: width , height
int w, h;
// text to display
String label = "ВЫКЛ";
ImageButton(int x, int y, int w, int h, PImage[] stateImages){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.stateImages = stateImages;
}
void mousePressed(int mx, int my){
// check the cursor is within the button bounds
boolean isOver = ((mx >= x && mx <= x + w) && // check horizontal
(my >= y && my <= y + h) ); // check vertical
if(isOver){
min += 10;
stateIndex++;
if (min>60) {
min = 0;
stateIndex = 0;
label = "ВЫКЛ";
} else {
label = min + " Мин";
}
}
}
void draw(){
// if the images and index are valid
if(stateImages != null && stateIndex < stateImages.length){
image(stateImages[stateIndex], x, y, w, h);
}else{
println("error displaying button state image");
println("stateImages: ");
printArray(stateImages);
println("stateIndex: " + stateIndex);
}
// display text
text(label, x + 5, y + h - 8);
}
}
I'm trying to make a simple guess the number game and when the user guesses a number between a specific range from the actual answer it will draw a rectangle with a different colour. As of now i'm just testing and have created 2 paint methods and want to now how to call the method "paint2".
import java.awt.*;
import hsa.Console;
import javax.swing.JFrame;
import java.util.Random;
import java.awt.Canvas;
import java.awt.Graphics;
public class MashGuessTheNumber extends Canvas {
static Console c; // The output console
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("My Drawing");
Canvas canvas = new MashGuessTheNumber();
canvas.setSize(400, 400);
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
MashGuessTheNumber sm = new MashGuessTheNumber();
c = new Console();
int loop = 0;
while (loop == 0) { // loop used to continue looping the questions after one is answered
int answer = 0;
c.println("Welcome to the guess the number game!");
c.println("What is your name?");
String name = c.readLine();
c.print("why, hello there ");
c.println(name);
c.println("What diffuculty would you like to play?(easy/medium/hard)");
String diff = c.readLine();
if (diff.equalsIgnoreCase("easy")) {
// *Location of random number generator*
c.println("So you chose easy, huh");
c.println("I'm thinking of a number between 1 and 10");
int guess = 1;
answer = (int) (Math.random() * ((10 - 1) + 1));
while (guess != answer) {
c.println("What is it?");
guess = c.readInt();
c.println(answer);
if ((((guess - answer) < 3) && ((guess - answer) > 0))
|| (((answer - guess) < 3) && ((answer - guess) > 0))) {
c.println("EXTREMELY HOT");
}
}
if (guess == answer) {
c.println("You did it!");
}
}
if (diff.equalsIgnoreCase("medium")) {
// *Location of random number generator*
c.println("So you chose medium, huh");
c.println("I'm thinking of a number between 1 and 100");
c.println("What is it?");
String guess = c.readLine();
answer = (int) (Math.random() * ((100 - 1) + 1));
}
if (diff.equalsIgnoreCase("hard")) {
// *Location of random number generator*
c.println("So you chose hard, huh");
c.println("I'm thinking of a number between 1 and 1000");
c.println("What is it?");
String guess = c.readLine();
answer = (int) (Math.random() * ((1000 - 1) + 1));
}
c.println("Would you like to play again?(y/n)"); // if answerd with "y" the loop will repeat
String cont = c.readLine();
if (cont.equalsIgnoreCase("y")) {
loop = 0;
} else {
for(int i=1;i<=24;i++){
c.println(" ");
c.setCursor(12, 30);
c.println("See you next time. Bye!");
loop = 1; // Stops the loop and says bye to the user
}
}
// Place your program here. 'c' is the output console
}
public void paint(Graphics g) {
int x[] = { 35, 75, 75, 35 };
int y[] = { 10, 10, 200, 200 };
g.setColor(Color.black);
int numberofpoints = 4;
g.drawPolygon(x, y, numberofpoints);
}
public void paint2(Graphics g) {
int x[] = { 35, 75, 75, 35 };
int y[] = { 10, 10, 200, 200 };
g.setColor(Color.blue);
int numberofpoints = 4;
g.drawPolygon(x, y, numberofpoints);
}
// main method
} // MashGuessTheNumber class
I'm just trying to draw a different rectangle as of now over top the first one whenever I want and if there is another way that doesn't use two methods that would also be helpful
I'm not seeing where you call paint() at all, but I would make a second parameter that would act as a flag telling me whether to draw a blue or black rectangle. This would get rid of repeated code.
public void paint (Graphics g, Color color)
{
int x[] = {35, 75, 75, 35};
int y[] = {10, 10, 200, 200};
g.setColor (color);
int numberofpoints = 4;
g.drawPolygon (x, y, numberofpoints);
}
I don't know if 'Color' is the correct object to use to pass in a color, but the point is to pass in something that will help you choose what color to make the rectangle so that way you don't have to write two very similar methods. You will call paint() like this:
if(some condition) {
paint(g, Color.blue);
}
else {
paint(g, Color.black);
}
In the Java Swing framework, you don't call Canvas.paint() yourself. But, you can use your own intance variables.
If you add an instance variable to your MashGuessNumber class
private Color myColor = Color.BLACK;
Then modify your if statement
if (count == answer) {
System.out.println("You did it1);
myColor == Color.BLUE;
repaint(); // already a Canvas method
}
Then modify your paint() method
public void paint (Graphics g, Color color)
{
int x[] = {35, 75, 75, 35};
int y[] = {10, 10, 200, 200};
g.setColor (myColor);
int numberofpoints = 4;
g.drawPolygon (x, y, numberofpoints);
}
This should do the trick.
The call to repaint() tells Swing to redraw the canvas by calling your paint() method. Otherwise the system would have no way of knowing that your Canvas needs to be redrawn.
I've got 8 ovals all set to the same colour. After a second I want the first oval to change colours and then after another second i want the first oval to go back to its original colour and then change the second ovals colour. I've drawn the circles and I've tried implementing a thread but i think it's not executing...
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Circle extends JPanel implements Runnable
{
Graphics g;
Thread t = new Thread();
int[][] fillCircles = new int[8][4];
#Override
public void paintComponent(Graphics g)
{
this.g = g;
super.paintComponent(this.g);
this.g.setColor(new java.awt.Color(237, 54, 26));
this.g.drawOval(300, 50, 100, 100);
this.g.drawOval(450, 125, 100, 100);
this.g.drawOval(500, 250, 100, 100);
this.g.drawOval(425, 375, 100, 100);
this.g.drawOval(300, 400, 100, 100);
this.g.drawOval(175, 350, 100, 100);
this.g.drawOval(125, 225, 100, 100);
this.g.drawOval(175, 100, 100, 100);
this.g.fillOval(300, 50, 100, 100);
this.g.fillOval(450, 125, 100, 100);
this.g.fillOval(500, 250, 100, 100);
this.g.fillOval(425, 375, 100, 100);
this.g.fillOval(300, 400, 100, 100);
this.g.fillOval(175, 350, 100, 100);
this.g.fillOval(125, 225, 100, 100);
this.g.fillOval(175, 100, 100, 100);
fillCircles[0][0] = 300;
fillCircles[0][1] = 50;
fillCircles[0][2] = 100;
fillCircles[0][3] = 100;
fillCircles[1][0] = 450;
fillCircles[1][1] = 125;
fillCircles[1][2] = 100;
fillCircles[1][3] = 100;
fillCircles[2][0] = 500;
fillCircles[2][1] = 250;
fillCircles[2][2] = 100;
fillCircles[2][3] = 100;
fillCircles[3][0] = 425;
fillCircles[3][1] = 375;
fillCircles[3][2] = 100;
fillCircles[3][3] = 100;
fillCircles[4][0] = 300;
fillCircles[4][1] = 400;
fillCircles[4][2] = 100;
fillCircles[4][3] = 100;
fillCircles[5][0] = 175;
fillCircles[5][1] = 350;
fillCircles[5][2] = 100;
fillCircles[5][3] = 100;
fillCircles[6][0] = 125;
fillCircles[6][1] = 225;
fillCircles[6][2] = 100;
fillCircles[6][3] = 100;
fillCircles[7][0] = 175;
fillCircles[7][1] = 100;
fillCircles[7][2] = 100;
fillCircles[7][3] = 100;
}
Circle () {
t.start();
}
public void run () {
int circle = 0;
try {
for (;;) {
Thread.sleep(1000);
if (circle > 0) {
this.g.setColor(new java.awt.Color(237, 54, 26));
circle--;
this.g.fillOval(fillCircles[circle][0], fillCircles[circle][1], fillCircles[circle][2], fillCircles[circle][2]);
circle++;
}
this.g.setColor(Color.red);
this.g.fillOval(fillCircles[circle][0], fillCircles[circle][1], fillCircles[circle][2], fillCircles[circle][2]);
circle++;
if (circle == 8) {
circle = 0;
}
}
} catch (InterruptedException e) {
System.out.println ("Thread Interrupted");
}
}
public static void main(String[] args) {
Circle c;
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(c=new Circle());
application.setSize(1200, 900);
application.setVisible(true);
}
}
You're just instantiating a Thread without giving it a Runnable, so it's not running what you're intending.
Thread t = new Thread();
should be
Thread t = new Thread(this);
this because the Circle is Runnable and the run() method is in it.
Regarding color changing, I would give you an idea.
When drawing the circles, check the current circle number/index against another variable which changes every second colorChangeIndex
if(circleIndex == colorChangeIndex){
// another color
else
//the default color
colorChangeIndex++; (modulo number of cricles to reset it)
The basic structure of your code is wrong. Painting should only be done in the paintComponent() method. You should never reference the Graphics object in your thread.
So, instead what you might want to do is keep an ArrayList of "circle" objects. This custom object will contain the circle to be painted and its color. Then in the paintComponent() method you just iterate through the ArrayList and paint all the circles in the ArrayList. Check out the DrawOnComponent example found in Custom Painting Approches.
After a second I want the first oval to change colours and then after another second i want the first oval to go back to its original colour and then change the second ovals colour.
So now you need to use a Swing Timer to animate the painting. When the Timer fires you reset the Color of the appropriate circle and then just repaint the panel.
I have a graphic which consists of a horizontal line of circles of different sizes at regular intervals. Here is the picture:
I am trying to recreate this graphic using recursion as opposed to the if statements used in my code but after trying, am unsure about how to do this. Would love some help, here is my code:
package weekFour;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class Circle extends JPanel {
private int circX = 10;
private static int windowW = 1700;
private static int windowH = 1000;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //smoothes out edges
Color c5 = new Color(50, 50, 50);
Color c4 = new Color(100, 100, 100);
Color c3 = new Color(150, 150, 150);
Color c2 = new Color(200, 200, 200);
Color c1= new Color(250, 250, 250);
for (int i = 0; i < 1; i++) {
g2.setColor(c1);
g2.fillOval(522 + 75*i, 138, 666, 666);
g2.setColor(c1);
g2.drawOval(522 + 75*i, 138, 666, 666);
}
for (int i = 0; i < 3; i++) {
g2.setColor(c2);
g2.fillOval(244 + 522*i, 365, 180, 180);
g2.setColor(c2);
g2.drawOval(244 + 522*i, 365, 180, 180);
}
for (int i = 0; i < 10; i++) {
g2.setColor(c3);
g2.fillOval(130 + 174*i, 428, 60, 60);
g2.setColor(c3);
g2.drawOval(130 + 174*i, 428, 60, 60);
}
for (int i = 0; i < 25; i++) {
g2.setColor(c4);
g2.fillOval(60 + 87*i, 444, 25, 25);
g2.setColor(c4);
g2.drawOval(60 + 87*i, 444, 25, 25);
}
for (int i = 0; i < 120; i++) {
g2.setColor(c5);
g2.fillOval(circX + 29*i, 450, 12, 12);
g2.setColor(c5);
g2.drawOval(circX + 29*i, 450, 12, 12);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MyTaskToo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Circle());
frame.setSize(windowW, windowH);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Thanks for your time.
This is how I went about this problem, although we had to do it with green circles as opposed to grey circles but it's not that different.
N.B: Sorry for the appealing comments for sometimes trivial things but we get marks for commenting and it is better to be safe than sorry. Maybe they change you some insight into the thought process.
Here is the main method that starts the programme and sets out the window information.
public class Q2Main {
public static void main(String[] args) {
// here we are just setting out the window end putting the circles drawin in Q2Circles into this window.
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(1000, 500);
window.getContentPane().add(new Q2Circles(5));
window.setVisible(true);
}}
This is where the magic happens:
public class Q2Circles extends JPanel {
// this allows the user to specify how many loops of recursion they want the programme to complete before finishing
int recursionsToDo;
public Q2Circles(int recursionMax){
super();
recursionsToDo = recursionMax;
}
/*
this method is automatically called when we run the constructor as it inherits from the JFram superclass. here
we are setting out the size of the circle by getting the size of the window to make it proportional to the rest
of the screen and circles.
we then pass these values into the drawCircle method to draw the circle
*/
public void paintComponent(Graphics g){
Rectangle rectangle = this.getBounds();
int diameter = rectangle.width/3;
int centerPoint = rectangle.width/2;
drawCircle(g, 1, centerPoint, diameter);
}
/*
This method is where the magic of the programme really takes place. first of all we make sure we haven't completed
the necessary recursions. we the set the color by dividing it by the amount of times we have recursed, this will
have the affect of getting darker the more times the method recurses. we then sset the color. finaly we fill the
oval (draw the circle). because we want to move depending on the times it has recursed and size of the previous
we do it based on the size of the elements from the previous call to this method. Getting the right numbers
though was just alot of trial and error.
we then increment the recursion counter so that we know how many times we have recursed and that can then be
used at different points where needed. e.g for setting the color.
each recursive call used the dimension of the other recursive calls to make the whole picture. Although the
first recursive call creates the circles on the right of the screen. the second call draws the circle on the
left of the screen and the last one does the circles in the middle, they all use eachothers values to make it
complete. without one recursive step, more is missing than just what is created by that recursive call on its own.
in all honesty though, there is alot of duplication, like the large middlecircle.
*/
public void drawCircle(Graphics g, int amountOfRecursions, int center, int diameter){
if (amountOfRecursions <= recursionsToDo){
int recursionsCount = amountOfRecursions;
int greenColor = Math.round(225 / (amountOfRecursions));
g.setColor(new Color(0, greenColor, 0));
g.fillOval(center - (diameter/2), 200 - (diameter/2), diameter, diameter);
recursionsCount++;
drawCircle(g, recursionsCount, Math.round(center + diameter), diameter/3);
drawCircle(g, recursionsCount, Math.round(center - diameter), diameter/3);
drawCircle(g, recursionsCount, Math.round(center), diameter/3);
}
}}
How to 'fill' Unicode characters in labels in Swing?
I'm trying to make a user interface for the chess program I've recently programmed
(with chess pieces something like seen above). In it I'm using unicode characters to represent my chess pieces (\u2654 through \u265F).
The problem is as follows:
When I set the background of my chess piece JLabel to something like white, the entire label is filled (in my case it's a 50*50px square of white with the character on top). This leads to my pieces looking like tiles instead of just their pictures.
When I set the label to opaque, I just get a cookie cutter version of my chess piece, not one with its insides filled. E.G.
Is there a way to fill only the character?
If not I guess I'll make a sprite sheet but I like this because I can use the chess pieces' toString() methods for the labels.
Code
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 50);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(
String s, Container c, boolean randomColor) {
JLabel l = new JLabel(s);
l.setFont(font);
if (randomColor) {
int r = rnd.nextInt(255);
int g = rnd.nextInt(255);
int b = rnd.nextInt(255);
l.setForeground(new Color(r,g,b));
l.setBackground(new Color(255-r,255-g,255-b));
l.setOpaque(true);
}
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0,6,4,4));
String[] pieces = {
"\u2654","\u2655","\u2656","\u2657","\u2658","\u2659",
"\u265A","\u265B","\u265C","\u265D","\u265E","\u265F"
};
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,false);
}
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,true);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
The two rows are generated through the sorcery of Java-2D. The trick is to:
Ignore the 'black' chess pieces on the basis that our color is actually coming from 'the spaces contained by the shape'. Those are larger in the white chess pieces.
Create a GlyphVector that represents the shape of the character. This is important for further operations in Java-2D.
Create a Rectangle the size of the image.
subtract() the shape of the character from the shape of the image.
Break that altered shape into regions.
Fill the regions with the background color, but skip the single region that starts at 0.0,0.0 (representing the outermost region which we need transparent).
Finally, fill the shape of the character itself using the outline color.
Code
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.*;
class ChessBoard {
static Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 50);
static Random rnd = new Random();
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
System.out.println(ii++ + " \t" + coords[0] + "," + coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static void addColoredUnicodeCharToContainer(
String s, Container c,
Color bgColor, Color outlineColor, boolean blackSquare) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, s);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
System.out.println("shape rect: " + r);
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
System.out.println("Box2D " + trans);
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.setColor(bgColor);
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
JLabel l = new JLabel(new ImageIcon(bi), JLabel.CENTER);
Color bg = (blackSquare ? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
boolean blackSquare = false;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(203,203,197),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
blackSquare = !blackSquare;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(192,142,60),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
Chess Board
This is what it might look like as a Chess Board (22.81 Kb).
Sprite sets
Sprite sets of chess pieces (64x64 pixel) rendered from Unicode characters - as a PNG with transparent BG. Each has 6 columns for the pieces x 2 rows for the opponents (total size 384x128 pixels).
Chess pieces with solid fill (bronze/pewter) (11.64Kb).
Chess pieces with gradient fill (gold/silver) (13.61Kb).
Chess pieces with gradient fill (darker cyan/magenta) (13.44Kb).
Code for Chess Board & Sprite Set
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.util.*;
import java.util.logging.*;
class ChessBoard {
/**
* Unicodes for chess pieces.
*/
static final String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
static final int KING = 0, QUEEN = 1, CASTLE = 2,
BISHOP = 3, KNIGHT = 4, PAWN = 5;
public static final int[] order = new int[]{
CASTLE, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, CASTLE
};
/*
* Colors..
*/
public static final Color outlineColor = Color.DARK_GRAY;
public static final Color[] pieceColors = {
new Color(203, 203, 197), new Color(192, 142, 60)
};
static final int WHITE = 0, BLACK = 1;
/*
* Font. The images use the font sizeXsize.
*/
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static BufferedImage getImageForChessPiece(
int piece, int side, boolean gradient) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, pieces[piece]);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
g.setColor(pieceColors[side]);
Color baseColor = pieceColors[side];
if (gradient) {
Color c1 = baseColor.brighter();
Color c2 = baseColor;
GradientPaint gp = new GradientPaint(
sz/2-(r.width/4), sz/2-(r.height/4), c1,
sz/2+(r.width/4), sz/2+(r.height/4), c2,
false);
g.setPaint(gp);
} else {
g.setColor(baseColor);
}
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
return bi;
}
public static void addColoredUnicodeCharToContainer(
Container c,
int piece,
int side,
Color bg,
boolean gradient) {
JLabel l = new JLabel(
new ImageIcon(getImageForChessPiece(piece, side, gradient)),
JLabel.CENTER);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void addPiecesToContainer(
Container c,
int intialSquareColor,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side,
intialSquareColor++%2 == BLACK ? Color.BLACK : Color.WHITE,
gradient);
}
}
public static void addPiecesToContainer(
Container c,
Color bg,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side, bg, gradient);
}
}
public static void addBlankLabelRow(Container c, int initialSquareColor) {
for (int ii = 0; ii < 8; ii++) {
JLabel l = new JLabel();
Color bg = (initialSquareColor++ % 2 == BLACK
? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
}
public static void main(String[] args) {
final int[] pawnRow = new int[]{
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN
};
Runnable r = new Runnable() {
#Override
public void run() {
int gradient = JOptionPane.showConfirmDialog(
null, "Use gradient fille color?");
boolean gradientFill = gradient == JOptionPane.OK_OPTION;
JPanel gui = new JPanel(new GridLayout(0, 8, 0, 0));
gui.setBorder(new BevelBorder(
BevelBorder.LOWERED,
Color.GRAY.brighter(), Color.GRAY,
Color.GRAY.darker(), Color.GRAY));
// set up a chess board
addPiecesToContainer(gui, WHITE, BLACK, order, gradientFill);
addPiecesToContainer(gui, BLACK, BLACK, pawnRow, gradientFill);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addPiecesToContainer(gui, WHITE, WHITE, pawnRow, gradientFill);
addPiecesToContainer(gui, BLACK, WHITE, order, gradientFill);
JOptionPane.showMessageDialog(
null,
gui,
"Chessboard",
JOptionPane.INFORMATION_MESSAGE);
JPanel tileSet = new JPanel(new GridLayout(0, 6, 0, 0));
tileSet.setOpaque(false);
int[] tileSetOrder = new int[]{
KING, QUEEN, CASTLE, KNIGHT, BISHOP, PAWN
};
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
BLACK,
tileSetOrder,
gradientFill);
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
WHITE,
tileSetOrder,
gradientFill);
int result = JOptionPane.showConfirmDialog(
null,
tileSet,
"Save this tileset?",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
BufferedImage bi = new BufferedImage(
tileSet.getWidth(),
tileSet.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
tileSet.paint(g);
g.dispose();
String gradientString = gradientFill ? "gradient" : "solid";
File f = new File(
"chess-pieces-tileset-" + gradientString + ".png");
try {
ImageIO.write(bi, "png", f);
Desktop.getDesktop().open(f);
} catch (IOException ex) {
Logger.getLogger(
ChessBoard.class.getName()).log(
Level.SEVERE, null, ex);
}
}
}
};
SwingUtilities.invokeLater(r);
}
}
See also
Developed out of the GlyphVector code as seen in this answer.
Resulted in UGlys - Unicode Glyphs at GitHub.
The problem I see is that the glyphs were designed to easily distinguish traditional black and white chess pieces. Note also the variation in font design. You may be able to create hue-themed pieces that preserve the black and white distinction using the HSB color space. Green and cyan are pictured below.
Addendum: For reference, here's a Mac OS X screenshot of #Andrew's glyph shape approach. Note the benefit of #Andrew's use of RenderingHints as the image is scaled.
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/a/18691662/230513 */
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(String s, Container c) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
c.add(l);
}
public static void addWhite(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 1, 1));
l.setBackground(Color.getHSBColor(h, 3 / 8f, 5 / 8f));
c.add(l);
}
public static void addBlack(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 5 / 8f, 3 / 8f));
l.setBackground(Color.getHSBColor(h, 7 / 8f, 7 / 8f));
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] white = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
String[] black = {
"\u265A", "\u265B", "\u265C", "\u265D", "\u265E", "\u265F"
};
for (String piece : white) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : white) {
addWhite(piece, gui, 2 / 6f);
}
for (String piece : black) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : black) {
addBlack(piece, gui, 3 / 6f);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
In the end, I found making a spritesheet to be the easier and simpler way to go about the problem. Each piece now corresponds to a graphic within the spritesheet instead of a character/glyph. Because of this, the pieces can't be resized as nicely but that's not the biggest deal.
#Andrew Thompson's idea with the GlyphVector seemed promising, but the matter of separating inner white space from outer white space remains difficult.
One (inefficient) idea I still have is to make a ton of chess piece glyphs starting from a very small font size and with a foregound color of white:
for (int i = 1; i < BOARD_WIDTH/8) {
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.white);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, i));
add(chessPiece);
}
then add one last chess piece with a black foreground:
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.black);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, BOARD_WIDTH/8)));
add(chessPiece);
Note that I haven't tested this out.